Roo/bootstrap/Table.js
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @children Roo.bootstrap.Component 
859  * @parent none builder
860  * Bootstrap Body class
861  *
862  * @constructor
863  * Create a new body
864  * @param {Object} config The config object
865  */
866
867 Roo.bootstrap.Body = function(config){
868
869     config = config || {};
870
871     Roo.bootstrap.Body.superclass.constructor.call(this, config);
872     this.el = Roo.get(config.el ? config.el : document.body );
873     if (this.cls && this.cls.length) {
874         Roo.get(document.body).addClass(this.cls);
875     }
876 };
877
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
879
880     is_body : true,// just to make sure it's constructed?
881
882         autoCreate : {
883         cls: 'container'
884     },
885     onRender : function(ct, position)
886     {
887        /* Roo.log("Roo.bootstrap.Body - onRender");
888         if (this.cls && this.cls.length) {
889             Roo.get(document.body).addClass(this.cls);
890         }
891         // style??? xttr???
892         */
893     }
894
895
896
897
898 });
899 /*
900  * - LGPL
901  *
902  * button group
903  * 
904  */
905
906
907 /**
908  * @class Roo.bootstrap.ButtonGroup
909  * @extends Roo.bootstrap.Component
910  * Bootstrap ButtonGroup class
911  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
912  * 
913  * @cfg {String} size lg | sm | xs (default empty normal)
914  * @cfg {String} align vertical | justified  (default none)
915  * @cfg {String} direction up | down (default down)
916  * @cfg {Boolean} toolbar false | true
917  * @cfg {Boolean} btn true | false
918  * 
919  * 
920  * @constructor
921  * Create a new Input
922  * @param {Object} config The config object
923  */
924
925 Roo.bootstrap.ButtonGroup = function(config){
926     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 };
928
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
930     
931     size: '',
932     align: '',
933     direction: '',
934     toolbar: false,
935     btn: true,
936
937     getAutoCreate : function(){
938         var cfg = {
939             cls: 'btn-group',
940             html : null
941         };
942         
943         cfg.html = this.html || cfg.html;
944         
945         if (this.toolbar) {
946             cfg = {
947                 cls: 'btn-toolbar',
948                 html: null
949             };
950             
951             return cfg;
952         }
953         
954         if (['vertical','justified'].indexOf(this.align)!==-1) {
955             cfg.cls = 'btn-group-' + this.align;
956             
957             if (this.align == 'justified') {
958                 console.log(this.items);
959             }
960         }
961         
962         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963             cfg.cls += ' btn-group-' + this.size;
964         }
965         
966         if (this.direction == 'up') {
967             cfg.cls += ' dropup' ;
968         }
969         
970         return cfg;
971     },
972     /**
973      * Add a button to the group (similar to NavItem API.)
974      */
975     addItem : function(cfg)
976     {
977         var cn = new Roo.bootstrap.Button(cfg);
978         //this.register(cn);
979         cn.parentId = this.id;
980         cn.onRender(this.el, null);
981         return cn;
982     }
983    
984 });
985
986  /*
987  * - LGPL
988  *
989  * button
990  * 
991  */
992
993 /**
994  * @class Roo.bootstrap.Button
995  * @extends Roo.bootstrap.Component
996  * Bootstrap Button class
997  * @cfg {String} html The button content
998  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001  * @cfg {String} size (lg|sm|xs)
1002  * @cfg {String} tag (a|input|submit)
1003  * @cfg {String} href empty or href
1004  * @cfg {Boolean} disabled default false;
1005  * @cfg {Boolean} isClose default false;
1006  * @cfg {String} glyphicon depricated - use fa
1007  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008  * @cfg {String} badge text for badge
1009  * @cfg {String} theme (default|glow)  
1010  * @cfg {Boolean} inverse dark themed version
1011  * @cfg {Boolean} toggle is it a slidy toggle button
1012  * @cfg {Boolean} pressed   default null - if the button ahs active state
1013  * @cfg {String} ontext text for on slidy toggle state
1014  * @cfg {String} offtext text for off slidy toggle state
1015  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1016  * @cfg {Boolean} removeClass remove the standard class..
1017  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1018  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1020
1021  * @constructor
1022  * Create a new button
1023  * @param {Object} config The config object
1024  */
1025
1026
1027 Roo.bootstrap.Button = function(config){
1028     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1029     
1030     this.addEvents({
1031         // raw events
1032         /**
1033          * @event click
1034          * When a button is pressed
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "click" : true,
1039         /**
1040          * @event dblclick
1041          * When a button is double clicked
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "dblclick" : true,
1046          /**
1047          * @event toggle
1048          * After the button has been toggles
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          * @param {boolean} pressed (also available as button.pressed)
1052          */
1053         "toggle" : true
1054     });
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1058     html: false,
1059     active: false,
1060     weight: '',
1061     badge_weight: '',
1062     outline : false,
1063     size: '',
1064     tag: 'button',
1065     href: '',
1066     disabled: false,
1067     isClose: false,
1068     glyphicon: '',
1069     fa: '',
1070     badge: '',
1071     theme: 'default',
1072     inverse: false,
1073     
1074     toggle: false,
1075     ontext: 'ON',
1076     offtext: 'OFF',
1077     defaulton: true,
1078     preventDefault: true,
1079     removeClass: false,
1080     name: false,
1081     target: false,
1082     group : false,
1083      
1084     pressed : null,
1085      
1086     
1087     getAutoCreate : function(){
1088         
1089         var cfg = {
1090             tag : 'button',
1091             cls : 'roo-button',
1092             html: ''
1093         };
1094         
1095         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097             this.tag = 'button';
1098         } else {
1099             cfg.tag = this.tag;
1100         }
1101         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1102         
1103         if (this.toggle == true) {
1104             cfg={
1105                 tag: 'div',
1106                 cls: 'slider-frame roo-button',
1107                 cn: [
1108                     {
1109                         tag: 'span',
1110                         'data-on-text':'ON',
1111                         'data-off-text':'OFF',
1112                         cls: 'slider-button',
1113                         html: this.offtext
1114                     }
1115                 ]
1116             };
1117             // why are we validating the weights?
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 cfg.cls +=  ' ' + this.weight;
1120             }
1121             
1122             return cfg;
1123         }
1124         
1125         if (this.isClose) {
1126             cfg.cls += ' close';
1127             
1128             cfg["aria-hidden"] = true;
1129             
1130             cfg.html = "&times;";
1131             
1132             return cfg;
1133         }
1134              
1135         
1136         if (this.theme==='default') {
1137             cfg.cls = 'btn roo-button';
1138             
1139             //if (this.parentType != 'Navbar') {
1140             this.weight = this.weight.length ?  this.weight : 'default';
1141             //}
1142             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1143                 
1144                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146                 cfg.cls += ' btn-' + outline + weight;
1147                 if (this.weight == 'default') {
1148                     // BC
1149                     cfg.cls += ' btn-' + this.weight;
1150                 }
1151             }
1152         } else if (this.theme==='glow') {
1153             
1154             cfg.tag = 'a';
1155             cfg.cls = 'btn-glow roo-button';
1156             
1157             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1158                 
1159                 cfg.cls += ' ' + this.weight;
1160             }
1161         }
1162    
1163         
1164         if (this.inverse) {
1165             this.cls += ' inverse';
1166         }
1167         
1168         
1169         if (this.active || this.pressed === true) {
1170             cfg.cls += ' active';
1171         }
1172         
1173         if (this.disabled) {
1174             cfg.disabled = 'disabled';
1175         }
1176         
1177         if (this.items) {
1178             Roo.log('changing to ul' );
1179             cfg.tag = 'ul';
1180             this.glyphicon = 'caret';
1181             if (Roo.bootstrap.version == 4) {
1182                 this.fa = 'caret-down';
1183             }
1184             
1185         }
1186         
1187         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1188          
1189         //gsRoo.log(this.parentType);
1190         if (this.parentType === 'Navbar' && !this.parent().bar) {
1191             Roo.log('changing to li?');
1192             
1193             cfg.tag = 'li';
1194             
1195             cfg.cls = '';
1196             cfg.cn =  [{
1197                 tag : 'a',
1198                 cls : 'roo-button',
1199                 html : this.html,
1200                 href : this.href || '#'
1201             }];
1202             if (this.menu) {
1203                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1204                 cfg.cls += ' dropdown';
1205             }   
1206             
1207             delete cfg.html;
1208             
1209         }
1210         
1211        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1212         
1213         if (this.glyphicon) {
1214             cfg.html = ' ' + cfg.html;
1215             
1216             cfg.cn = [
1217                 {
1218                     tag: 'span',
1219                     cls: 'glyphicon glyphicon-' + this.glyphicon
1220                 }
1221             ];
1222         }
1223         if (this.fa) {
1224             cfg.html = ' ' + cfg.html;
1225             
1226             cfg.cn = [
1227                 {
1228                     tag: 'i',
1229                     cls: 'fa fas fa-' + this.fa
1230                 }
1231             ];
1232         }
1233         
1234         if (this.badge) {
1235             cfg.html += ' ';
1236             
1237             cfg.tag = 'a';
1238             
1239 //            cfg.cls='btn roo-button';
1240             
1241             cfg.href=this.href;
1242             
1243             var value = cfg.html;
1244             
1245             if(this.glyphicon){
1246                 value = {
1247                     tag: 'span',
1248                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1249                     html: this.html
1250                 };
1251             }
1252             if(this.fa){
1253                 value = {
1254                     tag: 'i',
1255                     cls: 'fa fas fa-' + this.fa,
1256                     html: this.html
1257                 };
1258             }
1259             
1260             var bw = this.badge_weight.length ? this.badge_weight :
1261                 (this.weight.length ? this.weight : 'secondary');
1262             bw = bw == 'default' ? 'secondary' : bw;
1263             
1264             cfg.cn = [
1265                 value,
1266                 {
1267                     tag: 'span',
1268                     cls: 'badge badge-' + bw,
1269                     html: this.badge
1270                 }
1271             ];
1272             
1273             cfg.html='';
1274         }
1275         
1276         if (this.menu) {
1277             cfg.cls += ' dropdown';
1278             cfg.html = typeof(cfg.html) != 'undefined' ?
1279                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280         }
1281         
1282         if (cfg.tag !== 'a' && this.href !== '') {
1283             throw "Tag must be a to set href.";
1284         } else if (this.href.length > 0) {
1285             cfg.href = this.href;
1286         }
1287         
1288         if(this.removeClass){
1289             cfg.cls = '';
1290         }
1291         
1292         if(this.target){
1293             cfg.target = this.target;
1294         }
1295         
1296         return cfg;
1297     },
1298     initEvents: function() {
1299        // Roo.log('init events?');
1300 //        Roo.log(this.el.dom);
1301         // add the menu...
1302         
1303         if (typeof (this.menu) != 'undefined') {
1304             this.menu.parentType = this.xtype;
1305             this.menu.triggerEl = this.el;
1306             this.addxtype(Roo.apply({}, this.menu));
1307         }
1308
1309
1310         if (this.el.hasClass('roo-button')) {
1311              this.el.on('click', this.onClick, this);
1312              this.el.on('dblclick', this.onDblClick, this);
1313         } else {
1314              this.el.select('.roo-button').on('click', this.onClick, this);
1315              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1316              
1317         }
1318         // why?
1319         if(this.removeClass){
1320             this.el.on('click', this.onClick, this);
1321         }
1322         
1323         if (this.group === true) {
1324              if (this.pressed === false || this.pressed === true) {
1325                 // nothing
1326             } else {
1327                 this.pressed = false;
1328                 this.setActive(this.pressed);
1329             }
1330             
1331         }
1332         
1333         this.el.enableDisplayMode();
1334         
1335     },
1336     onClick : function(e)
1337     {
1338         if (this.disabled) {
1339             return;
1340         }
1341         
1342         Roo.log('button on click ');
1343         if(this.href === '' || this.preventDefault){
1344             e.preventDefault();
1345         }
1346         
1347         if (this.group) {
1348             if (this.pressed) {
1349                 // do nothing -
1350                 return;
1351             }
1352             this.setActive(true);
1353             var pi = this.parent().items;
1354             for (var i = 0;i < pi.length;i++) {
1355                 if (this == pi[i]) {
1356                     continue;
1357                 }
1358                 if (pi[i].el.hasClass('roo-button')) {
1359                     pi[i].setActive(false);
1360                 }
1361             }
1362             this.fireEvent('click', this, e);            
1363             return;
1364         }
1365         
1366         if (this.pressed === true || this.pressed === false) {
1367             this.toggleActive(e);
1368         }
1369         
1370         
1371         this.fireEvent('click', this, e);
1372     },
1373     onDblClick: function(e)
1374     {
1375         if (this.disabled) {
1376             return;
1377         }
1378         if(this.preventDefault){
1379             e.preventDefault();
1380         }
1381         this.fireEvent('dblclick', this, e);
1382     },
1383     /**
1384      * Enables this button
1385      */
1386     enable : function()
1387     {
1388         this.disabled = false;
1389         this.el.removeClass('disabled');
1390         this.el.dom.removeAttribute("disabled");
1391     },
1392     
1393     /**
1394      * Disable this button
1395      */
1396     disable : function()
1397     {
1398         this.disabled = true;
1399         this.el.addClass('disabled');
1400         this.el.attr("disabled", "disabled")
1401     },
1402      /**
1403      * sets the active state on/off, 
1404      * @param {Boolean} state (optional) Force a particular state
1405      */
1406     setActive : function(v) {
1407         
1408         this.el[v ? 'addClass' : 'removeClass']('active');
1409         this.pressed = v;
1410     },
1411      /**
1412      * toggles the current active state 
1413      */
1414     toggleActive : function(e)
1415     {
1416         this.setActive(!this.pressed); // this modifies pressed...
1417         this.fireEvent('toggle', this, e, this.pressed);
1418     },
1419      /**
1420      * get the current active state
1421      * @return {boolean} true if it's active
1422      */
1423     isActive : function()
1424     {
1425         return this.el.hasClass('active');
1426     },
1427     /**
1428      * set the text of the first selected button
1429      */
1430     setText : function(str)
1431     {
1432         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433     },
1434     /**
1435      * get the text of the first selected button
1436      */
1437     getText : function()
1438     {
1439         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440     },
1441     
1442     setWeight : function(str)
1443     {
1444         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1446         this.weight = str;
1447         var outline = this.outline ? 'outline-' : '';
1448         if (str == 'default') {
1449             this.el.addClass('btn-default btn-outline-secondary');        
1450             return;
1451         }
1452         this.el.addClass('btn-' + outline + str);        
1453     }
1454     
1455     
1456 });
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1458
1459 Roo.bootstrap.Button.weights = [
1460     'default',
1461     'secondary' ,
1462     'primary',
1463     'success',
1464     'info',
1465     'warning',
1466     'danger',
1467     'link',
1468     'light',
1469     'dark'              
1470    
1471 ];/*
1472  * - LGPL
1473  *
1474  * column
1475  * 
1476  */
1477
1478 /**
1479  * @class Roo.bootstrap.Column
1480  * @extends Roo.bootstrap.Component
1481  * @children Roo.bootstrap.Component
1482  * Bootstrap Column class
1483  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491  *
1492  * 
1493  * @cfg {Boolean} hidden (true|false) hide the element
1494  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495  * @cfg {String} fa (ban|check|...) font awesome icon
1496  * @cfg {Number} fasize (1|2|....) font awsome size
1497
1498  * @cfg {String} icon (info-sign|check|...) glyphicon name
1499
1500  * @cfg {String} html content of column.
1501  * 
1502  * @constructor
1503  * Create a new Column
1504  * @param {Object} config The config object
1505  */
1506
1507 Roo.bootstrap.Column = function(config){
1508     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 };
1510
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1512     
1513     xs: false,
1514     sm: false,
1515     md: false,
1516     lg: false,
1517     xsoff: false,
1518     smoff: false,
1519     mdoff: false,
1520     lgoff: false,
1521     html: '',
1522     offset: 0,
1523     alert: false,
1524     fa: false,
1525     icon : false,
1526     hidden : false,
1527     fasize : 1,
1528     
1529     getAutoCreate : function(){
1530         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531         
1532         cfg = {
1533             tag: 'div',
1534             cls: 'column'
1535         };
1536         
1537         var settings=this;
1538         var sizes =   ['xs','sm','md','lg'];
1539         sizes.map(function(size ,ix){
1540             //Roo.log( size + ':' + settings[size]);
1541             
1542             if (settings[size+'off'] !== false) {
1543                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544             }
1545             
1546             if (settings[size] === false) {
1547                 return;
1548             }
1549             
1550             if (!settings[size]) { // 0 = hidden
1551                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1552                 // bootsrap4
1553                 for (var i = ix; i > -1; i--) {
1554                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1555                 }
1556                 
1557                 
1558                 return;
1559             }
1560             cfg.cls += ' col-' + size + '-' + settings[size] + (
1561                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562             );
1563             
1564         });
1565         
1566         if (this.hidden) {
1567             cfg.cls += ' hidden';
1568         }
1569         
1570         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571             cfg.cls +=' alert alert-' + this.alert;
1572         }
1573         
1574         
1575         if (this.html.length) {
1576             cfg.html = this.html;
1577         }
1578         if (this.fa) {
1579             var fasize = '';
1580             if (this.fasize > 1) {
1581                 fasize = ' fa-' + this.fasize + 'x';
1582             }
1583             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1584             
1585             
1586         }
1587         if (this.icon) {
1588             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1589         }
1590         
1591         return cfg;
1592     }
1593    
1594 });
1595
1596  
1597
1598  /*
1599  * - LGPL
1600  *
1601  * page container.
1602  * 
1603  */
1604
1605
1606 /**
1607  * @class Roo.bootstrap.Container
1608  * @extends Roo.bootstrap.Component
1609  * @children Roo.bootstrap.Component
1610  * @parent builder
1611  * Bootstrap Container class
1612  * @cfg {Boolean} jumbotron is it a jumbotron element
1613  * @cfg {String} html content of element
1614  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1616  * @cfg {String} header content of header (for panel)
1617  * @cfg {String} footer content of footer (for panel)
1618  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619  * @cfg {String} tag (header|aside|section) type of HTML tag.
1620  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621  * @cfg {String} fa font awesome icon
1622  * @cfg {String} icon (info-sign|check|...) glyphicon name
1623  * @cfg {Boolean} hidden (true|false) hide the element
1624  * @cfg {Boolean} expandable (true|false) default false
1625  * @cfg {Boolean} expanded (true|false) default true
1626  * @cfg {String} rheader contet on the right of header
1627  * @cfg {Boolean} clickable (true|false) default false
1628
1629  *     
1630  * @constructor
1631  * Create a new Container
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Container = function(config){
1636     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // raw events
1640          /**
1641          * @event expand
1642          * After the panel has been expand
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "expand" : true,
1647         /**
1648          * @event collapse
1649          * After the panel has been collapsed
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "collapse" : true,
1654         /**
1655          * @event click
1656          * When a element is chick
1657          * @param {Roo.bootstrap.Container} this
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1665     
1666     jumbotron : false,
1667     well: '',
1668     panel : '',
1669     header: '',
1670     footer : '',
1671     sticky: '',
1672     tag : false,
1673     alert : false,
1674     fa: false,
1675     icon : false,
1676     expandable : false,
1677     rheader : '',
1678     expanded : true,
1679     clickable: false,
1680   
1681      
1682     getChildContainer : function() {
1683         
1684         if(!this.el){
1685             return false;
1686         }
1687         
1688         if (this.panel.length) {
1689             return this.el.select('.panel-body',true).first();
1690         }
1691         
1692         return this.el;
1693     },
1694     
1695     
1696     getAutoCreate : function(){
1697         
1698         var cfg = {
1699             tag : this.tag || 'div',
1700             html : '',
1701             cls : ''
1702         };
1703         if (this.jumbotron) {
1704             cfg.cls = 'jumbotron';
1705         }
1706         
1707         
1708         
1709         // - this is applied by the parent..
1710         //if (this.cls) {
1711         //    cfg.cls = this.cls + '';
1712         //}
1713         
1714         if (this.sticky.length) {
1715             
1716             var bd = Roo.get(document.body);
1717             if (!bd.hasClass('bootstrap-sticky')) {
1718                 bd.addClass('bootstrap-sticky');
1719                 Roo.select('html',true).setStyle('height', '100%');
1720             }
1721              
1722             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1723         }
1724         
1725         
1726         if (this.well.length) {
1727             switch (this.well) {
1728                 case 'lg':
1729                 case 'sm':
1730                     cfg.cls +=' well well-' +this.well;
1731                     break;
1732                 default:
1733                     cfg.cls +=' well';
1734                     break;
1735             }
1736         }
1737         
1738         if (this.hidden) {
1739             cfg.cls += ' hidden';
1740         }
1741         
1742         
1743         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744             cfg.cls +=' alert alert-' + this.alert;
1745         }
1746         
1747         var body = cfg;
1748         
1749         if (this.panel.length) {
1750             cfg.cls += ' panel panel-' + this.panel;
1751             cfg.cn = [];
1752             if (this.header.length) {
1753                 
1754                 var h = [];
1755                 
1756                 if(this.expandable){
1757                     
1758                     cfg.cls = cfg.cls + ' expandable';
1759                     
1760                     h.push({
1761                         tag: 'i',
1762                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1763                     });
1764                     
1765                 }
1766                 
1767                 h.push(
1768                     {
1769                         tag: 'span',
1770                         cls : 'panel-title',
1771                         html : (this.expandable ? '&nbsp;' : '') + this.header
1772                     },
1773                     {
1774                         tag: 'span',
1775                         cls: 'panel-header-right',
1776                         html: this.rheader
1777                     }
1778                 );
1779                 
1780                 cfg.cn.push({
1781                     cls : 'panel-heading',
1782                     style : this.expandable ? 'cursor: pointer' : '',
1783                     cn : h
1784                 });
1785                 
1786             }
1787             
1788             body = false;
1789             cfg.cn.push({
1790                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1791                 html : this.html
1792             });
1793             
1794             
1795             if (this.footer.length) {
1796                 cfg.cn.push({
1797                     cls : 'panel-footer',
1798                     html : this.footer
1799                     
1800                 });
1801             }
1802             
1803         }
1804         
1805         if (body) {
1806             body.html = this.html || cfg.html;
1807             // prefix with the icons..
1808             if (this.fa) {
1809                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810             }
1811             if (this.icon) {
1812                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1813             }
1814             
1815             
1816         }
1817         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818             cfg.cls =  'container';
1819         }
1820         
1821         return cfg;
1822     },
1823     
1824     initEvents: function() 
1825     {
1826         if(this.expandable){
1827             var headerEl = this.headerEl();
1828         
1829             if(headerEl){
1830                 headerEl.on('click', this.onToggleClick, this);
1831             }
1832         }
1833         
1834         if(this.clickable){
1835             this.el.on('click', this.onClick, this);
1836         }
1837         
1838     },
1839     
1840     onToggleClick : function()
1841     {
1842         var headerEl = this.headerEl();
1843         
1844         if(!headerEl){
1845             return;
1846         }
1847         
1848         if(this.expanded){
1849             this.collapse();
1850             return;
1851         }
1852         
1853         this.expand();
1854     },
1855     
1856     expand : function()
1857     {
1858         if(this.fireEvent('expand', this)) {
1859             
1860             this.expanded = true;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1863             
1864             this.el.select('.panel-body',true).first().removeClass('hide');
1865             
1866             var toggleEl = this.toggleEl();
1867
1868             if(!toggleEl){
1869                 return;
1870             }
1871
1872             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1873         }
1874         
1875     },
1876     
1877     collapse : function()
1878     {
1879         if(this.fireEvent('collapse', this)) {
1880             
1881             this.expanded = false;
1882             
1883             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884             this.el.select('.panel-body',true).first().addClass('hide');
1885         
1886             var toggleEl = this.toggleEl();
1887
1888             if(!toggleEl){
1889                 return;
1890             }
1891
1892             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1893         }
1894     },
1895     
1896     toggleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-heading .fa',true).first();
1903     },
1904     
1905     headerEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading',true).first()
1912     },
1913     
1914     bodyEl : function()
1915     {
1916         if(!this.el || !this.panel.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-body',true).first()
1921     },
1922     
1923     titleEl : function()
1924     {
1925         if(!this.el || !this.panel.length || !this.header.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-title',true).first();
1930     },
1931     
1932     setTitle : function(v)
1933     {
1934         var titleEl = this.titleEl();
1935         
1936         if(!titleEl){
1937             return;
1938         }
1939         
1940         titleEl.dom.innerHTML = v;
1941     },
1942     
1943     getTitle : function()
1944     {
1945         
1946         var titleEl = this.titleEl();
1947         
1948         if(!titleEl){
1949             return '';
1950         }
1951         
1952         return titleEl.dom.innerHTML;
1953     },
1954     
1955     setRightTitle : function(v)
1956     {
1957         var t = this.el.select('.panel-header-right',true).first();
1958         
1959         if(!t){
1960             return;
1961         }
1962         
1963         t.dom.innerHTML = v;
1964     },
1965     
1966     onClick : function(e)
1967     {
1968         e.preventDefault();
1969         
1970         this.fireEvent('click', this, e);
1971     }
1972 });
1973
1974  /**
1975  * @class Roo.bootstrap.Card
1976  * @extends Roo.bootstrap.Component
1977  * @children Roo.bootstrap.Component
1978  * @licence LGPL
1979  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * @parent Roo.bootstrap.Card
2781  * @children Roo.bootstrap.Component
2782  * Bootstrap CardHeader class
2783  * @constructor
2784  * Create a new Card Header - that you can embed children into
2785  * @param {Object} config The config object
2786  */
2787
2788 Roo.bootstrap.CardHeader = function(config){
2789     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 };
2791
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2793     
2794     
2795     container_method : 'getCardHeader' 
2796     
2797      
2798     
2799     
2800    
2801 });
2802
2803  
2804
2805  /*
2806  * - LGPL
2807  *
2808  * Card footer - holder for the card footer elements.
2809  * 
2810  */
2811
2812 /**
2813  * @class Roo.bootstrap.CardFooter
2814  * @extends Roo.bootstrap.Element
2815  * @parent Roo.bootstrap.Card
2816  * @children Roo.bootstrap.Component
2817  * Bootstrap CardFooter class
2818  * 
2819  * @constructor
2820  * Create a new Card Footer - that you can embed children into
2821  * @param {Object} config The config object
2822  */
2823
2824 Roo.bootstrap.CardFooter = function(config){
2825     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 };
2827
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2829     
2830     
2831     container_method : 'getCardFooter' 
2832     
2833      
2834     
2835     
2836    
2837 });
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * Card header - holder for the card header elements.
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.CardImageTop
2850  * @extends Roo.bootstrap.Element
2851  * @parent Roo.bootstrap.Card
2852  * @children Roo.bootstrap.Component
2853  * Bootstrap CardImageTop class
2854  * 
2855  * @constructor
2856  * Create a new Card Image Top container
2857  * @param {Object} config The config object
2858  */
2859
2860 Roo.bootstrap.CardImageTop = function(config){
2861     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 };
2863
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2865     
2866    
2867     container_method : 'getCardImageTop' 
2868     
2869      
2870     
2871    
2872 });
2873
2874  
2875
2876  
2877 /*
2878 * Licence: LGPL
2879 */
2880
2881 /**
2882  * @class Roo.bootstrap.ButtonUploader
2883  * @extends Roo.bootstrap.Button
2884  * Bootstrap Button Uploader class - it's a button which when you add files to it
2885  *
2886  * 
2887  * @cfg {Number} errorTimeout default 3000
2888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2889  * @cfg {Array}  html The button text.
2890  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2891  *
2892  * @constructor
2893  * Create a new CardUploader
2894  * @param {Object} config The config object
2895  */
2896
2897 Roo.bootstrap.ButtonUploader = function(config){
2898     
2899  
2900     
2901     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2902     
2903      
2904      this.addEvents({
2905          // raw events
2906         /**
2907          * @event beforeselect
2908          * When button is pressed, before show upload files dialog is shown
2909          * @param {Roo.bootstrap.UploaderButton} this
2910          *
2911          */
2912         'beforeselect' : true,
2913          /**
2914          * @event fired when files have been selected, 
2915          * When a the download link is clicked
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          * @param {Array} Array of files that have been uploaded
2918          */
2919         'uploaded' : true
2920         
2921     });
2922 };
2923  
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2925     
2926      
2927     errorTimeout : 3000,
2928      
2929     images : false,
2930    
2931     fileCollection : false,
2932     allowBlank : true,
2933     
2934     multiple : true,
2935     
2936     getAutoCreate : function()
2937     {
2938        
2939         
2940         return  {
2941             cls :'div' ,
2942             cn : [
2943                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2944             ]
2945         };
2946            
2947          
2948     },
2949      
2950    
2951     initEvents : function()
2952     {
2953         
2954         Roo.bootstrap.Button.prototype.initEvents.call(this);
2955         
2956         
2957         
2958         
2959         
2960         this.urlAPI = (window.createObjectURL && window) || 
2961                                 (window.URL && URL.revokeObjectURL && URL) || 
2962                                 (window.webkitURL && webkitURL);
2963                         
2964         var im = {
2965             tag: 'input',
2966             type : 'file',
2967             cls : 'd-none  roo-card-upload-selector' 
2968           
2969         };
2970         if (this.multiple) {
2971             im.multiple = 'multiple';
2972         }
2973         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2974        
2975         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2976         
2977         this.selectorEl.on('change', this.onFileSelected, this);
2978          
2979          
2980        
2981     },
2982     
2983    
2984     onClick : function(e)
2985     {
2986         e.preventDefault();
2987         
2988         if ( this.fireEvent('beforeselect', this) === false) {
2989             return;
2990         }
2991          
2992         this.selectorEl.dom.click();
2993          
2994     },
2995     
2996     onFileSelected : function(e)
2997     {
2998         e.preventDefault();
2999         
3000         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3001             return;
3002         }
3003         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004         this.selectorEl.dom.value  = '';// hopefully reset..
3005         
3006         this.fireEvent('uploaded', this,  files );
3007         
3008     },
3009     
3010        
3011    
3012     
3013     /**
3014      * addCard - add an Attachment to the uploader
3015      * @param data - the data about the image to upload
3016      *
3017      * {
3018           id : 123
3019           title : "Title of file",
3020           is_uploaded : false,
3021           src : "http://.....",
3022           srcfile : { the File upload object },
3023           mimetype : file.type,
3024           preview : false,
3025           is_deleted : 0
3026           .. any other data...
3027         }
3028      *
3029      * 
3030     */
3031      
3032     reset: function()
3033     {
3034          
3035          this.selectorEl
3036     } 
3037     
3038     
3039     
3040     
3041 });
3042  /*
3043  * - LGPL
3044  *
3045  * image
3046  * 
3047  */
3048
3049
3050 /**
3051  * @class Roo.bootstrap.Img
3052  * @extends Roo.bootstrap.Component
3053  * Bootstrap Img class
3054  * @cfg {Boolean} imgResponsive false | true
3055  * @cfg {String} border rounded | circle | thumbnail
3056  * @cfg {String} src image source
3057  * @cfg {String} alt image alternative text
3058  * @cfg {String} href a tag href
3059  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060  * @cfg {String} xsUrl xs image source
3061  * @cfg {String} smUrl sm image source
3062  * @cfg {String} mdUrl md image source
3063  * @cfg {String} lgUrl lg image source
3064  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3065  * 
3066  * @constructor
3067  * Create a new Input
3068  * @param {Object} config The config object
3069  */
3070
3071 Roo.bootstrap.Img = function(config){
3072     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3073     
3074     this.addEvents({
3075         // img events
3076         /**
3077          * @event click
3078          * The img click event for the img.
3079          * @param {Roo.EventObject} e
3080          */
3081         "click" : true,
3082         /**
3083          * @event load
3084          * The when any image loads
3085          * @param {Roo.EventObject} e
3086          */
3087         "load" : true
3088     });
3089 };
3090
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3092     
3093     imgResponsive: true,
3094     border: '',
3095     src: 'about:blank',
3096     href: false,
3097     target: false,
3098     xsUrl: '',
3099     smUrl: '',
3100     mdUrl: '',
3101     lgUrl: '',
3102     backgroundContain : false,
3103
3104     getAutoCreate : function()
3105     {   
3106         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107             return this.createSingleImg();
3108         }
3109         
3110         var cfg = {
3111             tag: 'div',
3112             cls: 'roo-image-responsive-group',
3113             cn: []
3114         };
3115         var _this = this;
3116         
3117         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3118             
3119             if(!_this[size + 'Url']){
3120                 return;
3121             }
3122             
3123             var img = {
3124                 tag: 'img',
3125                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126                 html: _this.html || cfg.html,
3127                 src: _this[size + 'Url']
3128             };
3129             
3130             img.cls += ' roo-image-responsive-' + size;
3131             
3132             var s = ['xs', 'sm', 'md', 'lg'];
3133             
3134             s.splice(s.indexOf(size), 1);
3135             
3136             Roo.each(s, function(ss){
3137                 img.cls += ' hidden-' + ss;
3138             });
3139             
3140             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141                 cfg.cls += ' img-' + _this.border;
3142             }
3143             
3144             if(_this.alt){
3145                 cfg.alt = _this.alt;
3146             }
3147             
3148             if(_this.href){
3149                 var a = {
3150                     tag: 'a',
3151                     href: _this.href,
3152                     cn: [
3153                         img
3154                     ]
3155                 };
3156
3157                 if(this.target){
3158                     a.target = _this.target;
3159                 }
3160             }
3161             
3162             cfg.cn.push((_this.href) ? a : img);
3163             
3164         });
3165         
3166         return cfg;
3167     },
3168     
3169     createSingleImg : function()
3170     {
3171         var cfg = {
3172             tag: 'img',
3173             cls: (this.imgResponsive) ? 'img-responsive' : '',
3174             html : null,
3175             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3176         };
3177         
3178         if (this.backgroundContain) {
3179             cfg.cls += ' background-contain';
3180         }
3181         
3182         cfg.html = this.html || cfg.html;
3183         
3184         if (this.backgroundContain) {
3185             cfg.style="background-image: url(" + this.src + ')';
3186         } else {
3187             cfg.src = this.src || cfg.src;
3188         }
3189         
3190         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191             cfg.cls += ' img-' + this.border;
3192         }
3193         
3194         if(this.alt){
3195             cfg.alt = this.alt;
3196         }
3197         
3198         if(this.href){
3199             var a = {
3200                 tag: 'a',
3201                 href: this.href,
3202                 cn: [
3203                     cfg
3204                 ]
3205             };
3206             
3207             if(this.target){
3208                 a.target = this.target;
3209             }
3210             
3211         }
3212         
3213         return (this.href) ? a : cfg;
3214     },
3215     
3216     initEvents: function() 
3217     {
3218         if(!this.href){
3219             this.el.on('click', this.onClick, this);
3220         }
3221         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222             this.el.on('load', this.onImageLoad, this);
3223         } else {
3224             // not sure if this works.. not tested
3225             this.el.select('img', true).on('load', this.onImageLoad, this);
3226         }
3227         
3228     },
3229     
3230     onClick : function(e)
3231     {
3232         Roo.log('img onclick');
3233         this.fireEvent('click', this, e);
3234     },
3235     onImageLoad: function(e)
3236     {
3237         Roo.log('img load');
3238         this.fireEvent('load', this, e);
3239     },
3240     
3241     /**
3242      * Sets the url of the image - used to update it
3243      * @param {String} url the url of the image
3244      */
3245     
3246     setSrc : function(url)
3247     {
3248         this.src =  url;
3249         
3250         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251             if (this.backgroundContain) {
3252                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3253             } else {
3254                 this.el.dom.src =  url;
3255             }
3256             return;
3257         }
3258         
3259         this.el.select('img', true).first().dom.src =  url;
3260     }
3261     
3262     
3263    
3264 });
3265
3266  /*
3267  * - LGPL
3268  *
3269  * image
3270  * 
3271  */
3272
3273
3274 /**
3275  * @class Roo.bootstrap.Link
3276  * @extends Roo.bootstrap.Component
3277  * @children Roo.bootstrap.Component
3278  * Bootstrap Link Class (eg. '<a href>')
3279  
3280  * @cfg {String} alt image alternative text
3281  * @cfg {String} href a tag href
3282  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283  * @cfg {String} html the content of the link.
3284  * @cfg {String} anchor name for the anchor link
3285  * @cfg {String} fa - favicon
3286
3287  * @cfg {Boolean} preventDefault (true | false) default false
3288
3289  * 
3290  * @constructor
3291  * Create a new Input
3292  * @param {Object} config The config object
3293  */
3294
3295 Roo.bootstrap.Link = function(config){
3296     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3297     
3298     this.addEvents({
3299         // img events
3300         /**
3301          * @event click
3302          * The img click event for the img.
3303          * @param {Roo.EventObject} e
3304          */
3305         "click" : true
3306     });
3307 };
3308
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3310     
3311     href: false,
3312     target: false,
3313     preventDefault: false,
3314     anchor : false,
3315     alt : false,
3316     fa: false,
3317
3318
3319     getAutoCreate : function()
3320     {
3321         var html = this.html || '';
3322         
3323         if (this.fa !== false) {
3324             html = '<i class="fa fa-' + this.fa + '"></i>';
3325         }
3326         var cfg = {
3327             tag: 'a'
3328         };
3329         // anchor's do not require html/href...
3330         if (this.anchor === false) {
3331             cfg.html = html;
3332             cfg.href = this.href || '#';
3333         } else {
3334             cfg.name = this.anchor;
3335             if (this.html !== false || this.fa !== false) {
3336                 cfg.html = html;
3337             }
3338             if (this.href !== false) {
3339                 cfg.href = this.href;
3340             }
3341         }
3342         
3343         if(this.alt !== false){
3344             cfg.alt = this.alt;
3345         }
3346         
3347         
3348         if(this.target !== false) {
3349             cfg.target = this.target;
3350         }
3351         
3352         return cfg;
3353     },
3354     
3355     initEvents: function() {
3356         
3357         if(!this.href || this.preventDefault){
3358             this.el.on('click', this.onClick, this);
3359         }
3360     },
3361     
3362     onClick : function(e)
3363     {
3364         if(this.preventDefault){
3365             e.preventDefault();
3366         }
3367         //Roo.log('img onclick');
3368         this.fireEvent('click', this, e);
3369     }
3370    
3371 });
3372
3373  /*
3374  * - LGPL
3375  *
3376  * header
3377  * 
3378  */
3379
3380 /**
3381  * @class Roo.bootstrap.Header
3382  * @extends Roo.bootstrap.Component
3383  * @children Roo.bootstrap.Component
3384  * Bootstrap Header class
3385  *
3386  * 
3387  * @cfg {String} html content of header
3388  * @cfg {Number} level (1|2|3|4|5|6) default 1
3389  * 
3390  * @constructor
3391  * Create a new Header
3392  * @param {Object} config The config object
3393  */
3394
3395
3396 Roo.bootstrap.Header  = function(config){
3397     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3398 };
3399
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3401     
3402     //href : false,
3403     html : false,
3404     level : 1,
3405     
3406     
3407     
3408     getAutoCreate : function(){
3409         
3410         
3411         
3412         var cfg = {
3413             tag: 'h' + (1 *this.level),
3414             html: this.html || ''
3415         } ;
3416         
3417         return cfg;
3418     }
3419    
3420 });
3421
3422  
3423
3424  /**
3425  * @class Roo.bootstrap.MenuMgr
3426  * @licence LGPL
3427  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3428  * @static
3429  */
3430 Roo.bootstrap.menu.Manager = function(){
3431    var menus, active, groups = {}, attached = false, lastShow = new Date();
3432
3433    // private - called when first menu is created
3434    function init(){
3435        menus = {};
3436        active = new Roo.util.MixedCollection();
3437        Roo.get(document).addKeyListener(27, function(){
3438            if(active.length > 0){
3439                hideAll();
3440            }
3441        });
3442    }
3443
3444    // private
3445    function hideAll(){
3446        if(active && active.length > 0){
3447            var c = active.clone();
3448            c.each(function(m){
3449                m.hide();
3450            });
3451        }
3452    }
3453
3454    // private
3455    function onHide(m){
3456        active.remove(m);
3457        if(active.length < 1){
3458            Roo.get(document).un("mouseup", onMouseDown);
3459             
3460            attached = false;
3461        }
3462    }
3463
3464    // private
3465    function onShow(m){
3466        var last = active.last();
3467        lastShow = new Date();
3468        active.add(m);
3469        if(!attached){
3470           Roo.get(document).on("mouseup", onMouseDown);
3471            
3472            attached = true;
3473        }
3474        if(m.parentMenu){
3475           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3476           m.parentMenu.activeChild = m;
3477        }else if(last && last.isVisible()){
3478           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3479        }
3480    }
3481
3482    // private
3483    function onBeforeHide(m){
3484        if(m.activeChild){
3485            m.activeChild.hide();
3486        }
3487        if(m.autoHideTimer){
3488            clearTimeout(m.autoHideTimer);
3489            delete m.autoHideTimer;
3490        }
3491    }
3492
3493    // private
3494    function onBeforeShow(m){
3495        var pm = m.parentMenu;
3496        if(!pm && !m.allowOtherMenus){
3497            hideAll();
3498        }else if(pm && pm.activeChild && active != m){
3499            pm.activeChild.hide();
3500        }
3501    }
3502
3503    // private this should really trigger on mouseup..
3504    function onMouseDown(e){
3505         Roo.log("on Mouse Up");
3506         
3507         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3508             Roo.log("MenuManager hideAll");
3509             hideAll();
3510             e.stopEvent();
3511         }
3512         
3513         
3514    }
3515
3516    // private
3517    function onBeforeCheck(mi, state){
3518        if(state){
3519            var g = groups[mi.group];
3520            for(var i = 0, l = g.length; i < l; i++){
3521                if(g[i] != mi){
3522                    g[i].setChecked(false);
3523                }
3524            }
3525        }
3526    }
3527
3528    return {
3529
3530        /**
3531         * Hides all menus that are currently visible
3532         */
3533        hideAll : function(){
3534             hideAll();  
3535        },
3536
3537        // private
3538        register : function(menu){
3539            if(!menus){
3540                init();
3541            }
3542            menus[menu.id] = menu;
3543            menu.on("beforehide", onBeforeHide);
3544            menu.on("hide", onHide);
3545            menu.on("beforeshow", onBeforeShow);
3546            menu.on("show", onShow);
3547            var g = menu.group;
3548            if(g && menu.events["checkchange"]){
3549                if(!groups[g]){
3550                    groups[g] = [];
3551                }
3552                groups[g].push(menu);
3553                menu.on("checkchange", onCheck);
3554            }
3555        },
3556
3557         /**
3558          * Returns a {@link Roo.menu.Menu} object
3559          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3560          * be used to generate and return a new Menu instance.
3561          */
3562        get : function(menu){
3563            if(typeof menu == "string"){ // menu id
3564                return menus[menu];
3565            }else if(menu.events){  // menu instance
3566                return menu;
3567            }
3568            /*else if(typeof menu.length == 'number'){ // array of menu items?
3569                return new Roo.bootstrap.Menu({items:menu});
3570            }else{ // otherwise, must be a config
3571                return new Roo.bootstrap.Menu(menu);
3572            }
3573            */
3574            return false;
3575        },
3576
3577        // private
3578        unregister : function(menu){
3579            delete menus[menu.id];
3580            menu.un("beforehide", onBeforeHide);
3581            menu.un("hide", onHide);
3582            menu.un("beforeshow", onBeforeShow);
3583            menu.un("show", onShow);
3584            var g = menu.group;
3585            if(g && menu.events["checkchange"]){
3586                groups[g].remove(menu);
3587                menu.un("checkchange", onCheck);
3588            }
3589        },
3590
3591        // private
3592        registerCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                if(!groups[g]){
3596                    groups[g] = [];
3597                }
3598                groups[g].push(menuItem);
3599                menuItem.on("beforecheckchange", onBeforeCheck);
3600            }
3601        },
3602
3603        // private
3604        unregisterCheckable : function(menuItem){
3605            var g = menuItem.group;
3606            if(g){
3607                groups[g].remove(menuItem);
3608                menuItem.un("beforecheckchange", onBeforeCheck);
3609            }
3610        }
3611    };
3612 }(); 
3613 /**
3614  * @class Roo.bootstrap.menu.Menu
3615  * @extends Roo.bootstrap.Component
3616  * @licence LGPL
3617  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3618  * @parent none
3619  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3620  * 
3621  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3623  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3624  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3625 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3626 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3627  
3628  * @constructor
3629  * Create a new Menu
3630  * @param {Object} config The config objectQ
3631  */
3632
3633
3634 Roo.bootstrap.menu.Menu = function(config){
3635     
3636     if (config.type == 'treeview') {
3637         // normally menu's are drawn attached to the document to handle layering etc..
3638         // however treeview (used by the docs menu is drawn into the parent element)
3639         this.container_method = 'getChildContainer'; 
3640     }
3641     
3642     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643     if (this.registerMenu && this.type != 'treeview')  {
3644         Roo.bootstrap.menu.Manager.register(this);
3645     }
3646     
3647     
3648     this.addEvents({
3649         /**
3650          * @event beforeshow
3651          * Fires before this menu is displayed (return false to block)
3652          * @param {Roo.menu.Menu} this
3653          */
3654         beforeshow : true,
3655         /**
3656          * @event beforehide
3657          * Fires before this menu is hidden (return false to block)
3658          * @param {Roo.menu.Menu} this
3659          */
3660         beforehide : true,
3661         /**
3662          * @event show
3663          * Fires after this menu is displayed
3664          * @param {Roo.menu.Menu} this
3665          */
3666         show : true,
3667         /**
3668          * @event hide
3669          * Fires after this menu is hidden
3670          * @param {Roo.menu.Menu} this
3671          */
3672         hide : true,
3673         /**
3674          * @event click
3675          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676          * @param {Roo.menu.Menu} this
3677          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678          * @param {Roo.EventObject} e
3679          */
3680         click : true,
3681         /**
3682          * @event mouseover
3683          * Fires when the mouse is hovering over this menu
3684          * @param {Roo.menu.Menu} this
3685          * @param {Roo.EventObject} e
3686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3687          */
3688         mouseover : true,
3689         /**
3690          * @event mouseout
3691          * Fires when the mouse exits this menu
3692          * @param {Roo.menu.Menu} this
3693          * @param {Roo.EventObject} e
3694          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3695          */
3696         mouseout : true,
3697         /**
3698          * @event itemclick
3699          * Fires when a menu item contained in this menu is clicked
3700          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701          * @param {Roo.EventObject} e
3702          */
3703         itemclick: true
3704     });
3705     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3706 };
3707
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3709     
3710    /// html : false,
3711    
3712     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3713     type: false,
3714     /**
3715      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3716      */
3717     registerMenu : true,
3718     
3719     menuItems :false, // stores the menu items..
3720     
3721     hidden:true,
3722         
3723     parentMenu : false,
3724     
3725     stopEvent : true,
3726     
3727     isLink : false,
3728     
3729     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3730     
3731     hideTrigger : false,
3732     
3733     align : 'tl-bl?',
3734     
3735     
3736     getChildContainer : function() {
3737         return this.el;  
3738     },
3739     
3740     getAutoCreate : function(){
3741          
3742         //if (['right'].indexOf(this.align)!==-1) {
3743         //    cfg.cn[1].cls += ' pull-right'
3744         //}
3745          
3746         var cfg = {
3747             tag : 'ul',
3748             cls : 'dropdown-menu shadow' ,
3749             style : 'z-index:1000'
3750             
3751         };
3752         
3753         if (this.type === 'submenu') {
3754             cfg.cls = 'submenu active';
3755         }
3756         if (this.type === 'treeview') {
3757             cfg.cls = 'treeview-menu';
3758         }
3759         
3760         return cfg;
3761     },
3762     initEvents : function() {
3763         
3764        // Roo.log("ADD event");
3765        // Roo.log(this.triggerEl.dom);
3766         if (this.triggerEl) {
3767             
3768             this.triggerEl.on('click', this.onTriggerClick, this);
3769             
3770             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3771             
3772             if (!this.hideTrigger) {
3773                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774                     // dropdown toggle on the 'a' in BS4?
3775                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3776                 } else {
3777                     this.triggerEl.addClass('dropdown-toggle');
3778                 }
3779             }
3780         }
3781         
3782         if (Roo.isTouch) {
3783             this.el.on('touchstart'  , this.onTouch, this);
3784         }
3785         this.el.on('click' , this.onClick, this);
3786
3787         this.el.on("mouseover", this.onMouseOver, this);
3788         this.el.on("mouseout", this.onMouseOut, this);
3789         
3790     },
3791     
3792     findTargetItem : function(e)
3793     {
3794         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3795         if(!t){
3796             return false;
3797         }
3798         //Roo.log(t);         Roo.log(t.id);
3799         if(t && t.id){
3800             //Roo.log(this.menuitems);
3801             return this.menuitems.get(t.id);
3802             
3803             //return this.items.get(t.menuItemId);
3804         }
3805         
3806         return false;
3807     },
3808     
3809     onTouch : function(e) 
3810     {
3811         Roo.log("menu.onTouch");
3812         //e.stopEvent(); this make the user popdown broken
3813         this.onClick(e);
3814     },
3815     
3816     onClick : function(e)
3817     {
3818         Roo.log("menu.onClick");
3819         
3820         var t = this.findTargetItem(e);
3821         if(!t || t.isContainer){
3822             return;
3823         }
3824         Roo.log(e);
3825         /*
3826         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3827             if(t == this.activeItem && t.shouldDeactivate(e)){
3828                 this.activeItem.deactivate();
3829                 delete this.activeItem;
3830                 return;
3831             }
3832             if(t.canActivate){
3833                 this.setActiveItem(t, true);
3834             }
3835             return;
3836             
3837             
3838         }
3839         */
3840        
3841         Roo.log('pass click event');
3842         
3843         t.onClick(e);
3844         
3845         this.fireEvent("click", this, t, e);
3846         
3847         var _this = this;
3848         
3849         if(!t.href.length || t.href == '#'){
3850             (function() { _this.hide(); }).defer(100);
3851         }
3852         
3853     },
3854     
3855     onMouseOver : function(e){
3856         var t  = this.findTargetItem(e);
3857         //Roo.log(t);
3858         //if(t){
3859         //    if(t.canActivate && !t.disabled){
3860         //        this.setActiveItem(t, true);
3861         //    }
3862         //}
3863         
3864         this.fireEvent("mouseover", this, e, t);
3865     },
3866     isVisible : function(){
3867         return !this.hidden;
3868     },
3869     onMouseOut : function(e){
3870         var t  = this.findTargetItem(e);
3871         
3872         //if(t ){
3873         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3874         //        this.activeItem.deactivate();
3875         //        delete this.activeItem;
3876         //    }
3877         //}
3878         this.fireEvent("mouseout", this, e, t);
3879     },
3880     
3881     
3882     /**
3883      * Displays this menu relative to another element
3884      * @param {String/HTMLElement/Roo.Element} element The element to align to
3885      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886      * the element (defaults to this.defaultAlign)
3887      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3888      */
3889     show : function(el, pos, parentMenu)
3890     {
3891         if (false === this.fireEvent("beforeshow", this)) {
3892             Roo.log("show canceled");
3893             return;
3894         }
3895         this.parentMenu = parentMenu;
3896         if(!this.el){
3897             this.render();
3898         }
3899         this.el.addClass('show'); // show otherwise we do not know how big we are..
3900          
3901         var xy = this.el.getAlignToXY(el, pos);
3902         
3903         // bl-tl << left align  below
3904         // tl-bl << left align 
3905         
3906         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907             // if it goes to far to the right.. -> align left.
3908             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3909         }
3910         if(xy[0] < 0){
3911             // was left align - go right?
3912             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3913         }
3914         
3915         // goes down the bottom
3916         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3917            xy[1]  < 0 ){
3918             var a = this.align.replace('?', '').split('-');
3919             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3920             
3921         }
3922         
3923         this.showAt(  xy , parentMenu, false);
3924     },
3925      /**
3926      * Displays this menu at a specific xy position
3927      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3929      */
3930     showAt : function(xy, parentMenu, /* private: */_e){
3931         this.parentMenu = parentMenu;
3932         if(!this.el){
3933             this.render();
3934         }
3935         if(_e !== false){
3936             this.fireEvent("beforeshow", this);
3937             //xy = this.el.adjustForConstraints(xy);
3938         }
3939         
3940         //this.el.show();
3941         this.hideMenuItems();
3942         this.hidden = false;
3943         if (this.triggerEl) {
3944             this.triggerEl.addClass('open');
3945         }
3946         
3947         this.el.addClass('show');
3948         
3949         
3950         
3951         // reassign x when hitting right
3952         
3953         // reassign y when hitting bottom
3954         
3955         // but the list may align on trigger left or trigger top... should it be a properity?
3956         
3957         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3958             this.el.setXY(xy);
3959         }
3960         
3961         this.focus();
3962         this.fireEvent("show", this);
3963     },
3964     
3965     focus : function(){
3966         return;
3967         if(!this.hidden){
3968             this.doFocus.defer(50, this);
3969         }
3970     },
3971
3972     doFocus : function(){
3973         if(!this.hidden){
3974             this.focusEl.focus();
3975         }
3976     },
3977
3978     /**
3979      * Hides this menu and optionally all parent menus
3980      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3981      */
3982     hide : function(deep)
3983     {
3984         if (false === this.fireEvent("beforehide", this)) {
3985             Roo.log("hide canceled");
3986             return;
3987         }
3988         this.hideMenuItems();
3989         if(this.el && this.isVisible()){
3990            
3991             if(this.activeItem){
3992                 this.activeItem.deactivate();
3993                 this.activeItem = null;
3994             }
3995             if (this.triggerEl) {
3996                 this.triggerEl.removeClass('open');
3997             }
3998             
3999             this.el.removeClass('show');
4000             this.hidden = true;
4001             this.fireEvent("hide", this);
4002         }
4003         if(deep === true && this.parentMenu){
4004             this.parentMenu.hide(true);
4005         }
4006     },
4007     
4008     onTriggerClick : function(e)
4009     {
4010         Roo.log('trigger click');
4011         
4012         var target = e.getTarget();
4013         
4014         Roo.log(target.nodeName.toLowerCase());
4015         
4016         if(target.nodeName.toLowerCase() === 'i'){
4017             e.preventDefault();
4018         }
4019         
4020     },
4021     
4022     onTriggerPress  : function(e)
4023     {
4024         Roo.log('trigger press');
4025         //Roo.log(e.getTarget());
4026        // Roo.log(this.triggerEl.dom);
4027        
4028         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029         var pel = Roo.get(e.getTarget());
4030         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031             Roo.log('is treeview or dropdown?');
4032             return;
4033         }
4034         
4035         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4036             return;
4037         }
4038         
4039         if (this.isVisible()) {
4040             Roo.log('hide');
4041             this.hide();
4042         } else {
4043             Roo.log('show');
4044             
4045             this.show(this.triggerEl, this.align, false);
4046         }
4047         
4048         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4049             e.stopEvent();
4050         }
4051         
4052     },
4053        
4054     
4055     hideMenuItems : function()
4056     {
4057         Roo.log("hide Menu Items");
4058         if (!this.el) { 
4059             return;
4060         }
4061         
4062         this.el.select('.open',true).each(function(aa) {
4063             
4064             aa.removeClass('open');
4065          
4066         });
4067     },
4068     addxtypeChild : function (tree, cntr) {
4069         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4070           
4071         this.menuitems.add(comp);
4072         return comp;
4073
4074     },
4075     getEl : function()
4076     {
4077         Roo.log(this.el);
4078         return this.el;
4079     },
4080     
4081     clear : function()
4082     {
4083         this.getEl().dom.innerHTML = '';
4084         this.menuitems.clear();
4085     }
4086 });
4087
4088  
4089  /**
4090  * @class Roo.bootstrap.menu.Item
4091  * @extends Roo.bootstrap.Component
4092  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093  * @parent Roo.bootstrap.menu.Menu
4094  * @licence LGPL
4095  * Bootstrap MenuItem class
4096  * 
4097  * @cfg {String} html the menu label
4098  * @cfg {String} href the link
4099  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4100  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4101  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4102  * @cfg {String} fa favicon to show on left of menu item.
4103  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4104  * 
4105  * 
4106  * @constructor
4107  * Create a new MenuItem
4108  * @param {Object} config The config object
4109  */
4110
4111
4112 Roo.bootstrap.menu.Item = function(config){
4113     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4114     this.addEvents({
4115         // raw events
4116         /**
4117          * @event click
4118          * The raw click event for the entire grid.
4119          * @param {Roo.bootstrap.menu.Item} this
4120          * @param {Roo.EventObject} e
4121          */
4122         "click" : true
4123     });
4124 };
4125
4126 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4127     
4128     href : false,
4129     html : false,
4130     preventDefault: false,
4131     isContainer : false,
4132     active : false,
4133     fa: false,
4134     
4135     getAutoCreate : function(){
4136         
4137         if(this.isContainer){
4138             return {
4139                 tag: 'li',
4140                 cls: 'dropdown-menu-item '
4141             };
4142         }
4143         var ctag = {
4144             tag: 'span',
4145             html: 'Link'
4146         };
4147         
4148         var anc = {
4149             tag : 'a',
4150             cls : 'dropdown-item',
4151             href : '#',
4152             cn : [  ]
4153         };
4154         
4155         if (this.fa !== false) {
4156             anc.cn.push({
4157                 tag : 'i',
4158                 cls : 'fa fa-' + this.fa
4159             });
4160         }
4161         
4162         anc.cn.push(ctag);
4163         
4164         
4165         var cfg= {
4166             tag: 'li',
4167             cls: 'dropdown-menu-item',
4168             cn: [ anc ]
4169         };
4170         if (this.parent().type == 'treeview') {
4171             cfg.cls = 'treeview-menu';
4172         }
4173         if (this.active) {
4174             cfg.cls += ' active';
4175         }
4176         
4177         
4178         
4179         anc.href = this.href || cfg.cn[0].href ;
4180         ctag.html = this.html || cfg.cn[0].html ;
4181         return cfg;
4182     },
4183     
4184     initEvents: function()
4185     {
4186         if (this.parent().type == 'treeview') {
4187             this.el.select('a').on('click', this.onClick, this);
4188         }
4189         
4190         if (this.menu) {
4191             this.menu.parentType = this.xtype;
4192             this.menu.triggerEl = this.el;
4193             this.menu = this.addxtype(Roo.apply({}, this.menu));
4194         }
4195         
4196     },
4197     onClick : function(e)
4198     {
4199         //Roo.log('item on click ');
4200         
4201         if(this.href === false || this.preventDefault){
4202             e.preventDefault();
4203         }
4204         //this.parent().hideMenuItems();
4205         
4206         this.fireEvent('click', this, e);
4207     },
4208     getEl : function()
4209     {
4210         return this.el;
4211     } 
4212 });
4213
4214  
4215
4216  
4217
4218   
4219 /**
4220  * @class Roo.bootstrap.menu.Separator
4221  * @extends Roo.bootstrap.Component
4222  * @licence LGPL
4223  * @parent Roo.bootstrap.menu.Menu
4224  * Bootstrap Separator class
4225  * 
4226  * @constructor
4227  * Create a new Separator
4228  * @param {Object} config The config object
4229  */
4230
4231
4232 Roo.bootstrap.menu.Separator = function(config){
4233     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4234 };
4235
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4237     
4238     getAutoCreate : function(){
4239         var cfg = {
4240             tag : 'li',
4241             cls: 'dropdown-divider divider'
4242         };
4243         
4244         return cfg;
4245     }
4246    
4247 });
4248
4249  
4250
4251  
4252 /*
4253 * Licence: LGPL
4254 */
4255
4256 /**
4257  * @class Roo.bootstrap.Modal
4258  * @extends Roo.bootstrap.Component
4259  * @parent none builder
4260  * @children Roo.bootstrap.Component
4261  * Bootstrap Modal class
4262  * @cfg {String} title Title of dialog
4263  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4265  * @cfg {Boolean} specificTitle default false
4266  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4267  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268  * @cfg {Boolean} animate default true
4269  * @cfg {Boolean} allow_close default true
4270  * @cfg {Boolean} fitwindow default false
4271  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274  * @cfg {String} size (sm|lg|xl) default empty
4275  * @cfg {Number} max_width set the max width of modal
4276  * @cfg {Boolean} editableTitle can the title be edited
4277
4278  *
4279  *
4280  * @constructor
4281  * Create a new Modal Dialog
4282  * @param {Object} config The config object
4283  */
4284
4285 Roo.bootstrap.Modal = function(config){
4286     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4287     this.addEvents({
4288         // raw events
4289         /**
4290          * @event btnclick
4291          * The raw btnclick event for the button
4292          * @param {Roo.EventObject} e
4293          */
4294         "btnclick" : true,
4295         /**
4296          * @event resize
4297          * Fire when dialog resize
4298          * @param {Roo.bootstrap.Modal} this
4299          * @param {Roo.EventObject} e
4300          */
4301         "resize" : true,
4302         /**
4303          * @event titlechanged
4304          * Fire when the editable title has been changed
4305          * @param {Roo.bootstrap.Modal} this
4306          * @param {Roo.EventObject} value
4307          */
4308         "titlechanged" : true 
4309         
4310     });
4311     this.buttons = this.buttons || [];
4312
4313     if (this.tmpl) {
4314         this.tmpl = Roo.factory(this.tmpl);
4315     }
4316
4317 };
4318
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4320
4321     title : 'test dialog',
4322
4323     buttons : false,
4324
4325     // set on load...
4326
4327     html: false,
4328
4329     tmp: false,
4330
4331     specificTitle: false,
4332
4333     buttonPosition: 'right',
4334
4335     allow_close : true,
4336
4337     animate : true,
4338
4339     fitwindow: false,
4340     
4341      // private
4342     dialogEl: false,
4343     bodyEl:  false,
4344     footerEl:  false,
4345     titleEl:  false,
4346     closeEl:  false,
4347
4348     size: '',
4349     
4350     max_width: 0,
4351     
4352     max_height: 0,
4353     
4354     fit_content: false,
4355     editableTitle  : false,
4356
4357     onRender : function(ct, position)
4358     {
4359         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4360
4361         if(!this.el){
4362             var cfg = Roo.apply({},  this.getAutoCreate());
4363             cfg.id = Roo.id();
4364             //if(!cfg.name){
4365             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4366             //}
4367             //if (!cfg.name.length) {
4368             //    delete cfg.name;
4369            // }
4370             if (this.cls) {
4371                 cfg.cls += ' ' + this.cls;
4372             }
4373             if (this.style) {
4374                 cfg.style = this.style;
4375             }
4376             this.el = Roo.get(document.body).createChild(cfg, position);
4377         }
4378         //var type = this.el.dom.type;
4379
4380
4381         if(this.tabIndex !== undefined){
4382             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4383         }
4384
4385         this.dialogEl = this.el.select('.modal-dialog',true).first();
4386         this.bodyEl = this.el.select('.modal-body',true).first();
4387         this.closeEl = this.el.select('.modal-header .close', true).first();
4388         this.headerEl = this.el.select('.modal-header',true).first();
4389         this.titleEl = this.el.select('.modal-title',true).first();
4390         this.footerEl = this.el.select('.modal-footer',true).first();
4391
4392         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4393         
4394         //this.el.addClass("x-dlg-modal");
4395
4396         if (this.buttons.length) {
4397             Roo.each(this.buttons, function(bb) {
4398                 var b = Roo.apply({}, bb);
4399                 b.xns = b.xns || Roo.bootstrap;
4400                 b.xtype = b.xtype || 'Button';
4401                 if (typeof(b.listeners) == 'undefined') {
4402                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4403                 }
4404
4405                 var btn = Roo.factory(b);
4406
4407                 btn.render(this.getButtonContainer());
4408
4409             },this);
4410         }
4411         // render the children.
4412         var nitems = [];
4413
4414         if(typeof(this.items) != 'undefined'){
4415             var items = this.items;
4416             delete this.items;
4417
4418             for(var i =0;i < items.length;i++) {
4419                 // we force children not to montor widnow resize  - as we do that for them.
4420                 items[i].monitorWindowResize = false;
4421                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628         // any layout/border etc.. resize..
4629         (function () {
4630             this.items.forEach( function(e) {
4631                 e.layout ? e.layout() : false;
4632
4633             });
4634         }).defer(100,this);
4635         
4636     },
4637
4638     show : function() {
4639
4640         if (!this.rendered) {
4641             this.render();
4642         }
4643         this.toggleHeaderInput(false);
4644         //this.el.setStyle('display', 'block');
4645         this.el.removeClass('hideing');
4646         this.el.dom.style.display='block';
4647         
4648         Roo.get(document.body).addClass('modal-open');
4649  
4650         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4651             
4652             (function(){
4653                 this.el.addClass('show');
4654                 this.el.addClass('in');
4655             }).defer(50, this);
4656         }else{
4657             this.el.addClass('show');
4658             this.el.addClass('in');
4659         }
4660
4661         // not sure how we can show data in here..
4662         //if (this.tmpl) {
4663         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4664         //}
4665
4666         Roo.get(document.body).addClass("x-body-masked");
4667         
4668         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4669         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4670         this.maskEl.dom.style.display = 'block';
4671         this.maskEl.addClass('show');
4672         
4673         
4674         this.resize();
4675         
4676         this.fireEvent('show', this);
4677
4678         // set zindex here - otherwise it appears to be ignored...
4679         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4680         
4681         
4682         // this is for children that are... layout.Border 
4683         (function () {
4684             this.items.forEach( function(e) {
4685                 e.layout ? e.layout() : false;
4686
4687             });
4688         }).defer(100,this);
4689
4690     },
4691     hide : function()
4692     {
4693         if(this.fireEvent("beforehide", this) !== false){
4694             
4695             this.maskEl.removeClass('show');
4696             
4697             this.maskEl.dom.style.display = '';
4698             Roo.get(document.body).removeClass("x-body-masked");
4699             this.el.removeClass('in');
4700             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4701
4702             if(this.animate){ // why
4703                 this.el.addClass('hideing');
4704                 this.el.removeClass('show');
4705                 (function(){
4706                     if (!this.el.hasClass('hideing')) {
4707                         return; // it's been shown again...
4708                     }
4709                     
4710                     this.el.dom.style.display='';
4711
4712                     Roo.get(document.body).removeClass('modal-open');
4713                     this.el.removeClass('hideing');
4714                 }).defer(150,this);
4715                 
4716             }else{
4717                 this.el.removeClass('show');
4718                 this.el.dom.style.display='';
4719                 Roo.get(document.body).removeClass('modal-open');
4720
4721             }
4722             this.fireEvent('hide', this);
4723         }
4724     },
4725     isVisible : function()
4726     {
4727         
4728         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4729         
4730     },
4731
4732     addButton : function(str, cb)
4733     {
4734
4735
4736         var b = Roo.apply({}, { html : str } );
4737         b.xns = b.xns || Roo.bootstrap;
4738         b.xtype = b.xtype || 'Button';
4739         if (typeof(b.listeners) == 'undefined') {
4740             b.listeners = { click : cb.createDelegate(this)  };
4741         }
4742
4743         var btn = Roo.factory(b);
4744
4745         btn.render(this.getButtonContainer());
4746
4747         return btn;
4748
4749     },
4750
4751     setDefaultButton : function(btn)
4752     {
4753         //this.el.select('.modal-footer').()
4754     },
4755
4756     resizeTo: function(w,h)
4757     {
4758         this.dialogEl.setWidth(w);
4759         
4760         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4761
4762         this.bodyEl.setHeight(h - diff);
4763         
4764         this.fireEvent('resize', this);
4765     },
4766     
4767     setContentSize  : function(w, h)
4768     {
4769
4770     },
4771     onButtonClick: function(btn,e)
4772     {
4773         //Roo.log([a,b,c]);
4774         this.fireEvent('btnclick', btn.name, e);
4775     },
4776      /**
4777      * Set the title of the Dialog
4778      * @param {String} str new Title
4779      */
4780     setTitle: function(str) {
4781         this.titleEl.dom.innerHTML = str;
4782         this.title = str;
4783     },
4784     /**
4785      * Set the body of the Dialog
4786      * @param {String} str new Title
4787      */
4788     setBody: function(str) {
4789         this.bodyEl.dom.innerHTML = str;
4790     },
4791     /**
4792      * Set the body of the Dialog using the template
4793      * @param {Obj} data - apply this data to the template and replace the body contents.
4794      */
4795     applyBody: function(obj)
4796     {
4797         if (!this.tmpl) {
4798             Roo.log("Error - using apply Body without a template");
4799             //code
4800         }
4801         this.tmpl.overwrite(this.bodyEl, obj);
4802     },
4803     
4804     getChildHeight : function(child_nodes)
4805     {
4806         if(
4807             !child_nodes ||
4808             child_nodes.length == 0
4809         ) {
4810             return 0;
4811         }
4812         
4813         var child_height = 0;
4814         
4815         for(var i = 0; i < child_nodes.length; i++) {
4816             
4817             /*
4818             * for modal with tabs...
4819             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4820                 
4821                 var layout_childs = child_nodes[i].childNodes;
4822                 
4823                 for(var j = 0; j < layout_childs.length; j++) {
4824                     
4825                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4826                         
4827                         var layout_body_childs = layout_childs[j].childNodes;
4828                         
4829                         for(var k = 0; k < layout_body_childs.length; k++) {
4830                             
4831                             if(layout_body_childs[k].classList.contains('navbar')) {
4832                                 child_height += layout_body_childs[k].offsetHeight;
4833                                 continue;
4834                             }
4835                             
4836                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4837                                 
4838                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4839                                 
4840                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4841                                     
4842                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4843                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4844                                         continue;
4845                                     }
4846                                     
4847                                 }
4848                                 
4849                             }
4850                             
4851                         }
4852                     }
4853                 }
4854                 continue;
4855             }
4856             */
4857             
4858             child_height += child_nodes[i].offsetHeight;
4859             // Roo.log(child_nodes[i].offsetHeight);
4860         }
4861         
4862         return child_height;
4863     },
4864     toggleHeaderInput : function(is_edit)
4865     {
4866         if (!this.editableTitle) {
4867             return; // not editable.
4868         }
4869         if (is_edit && this.is_header_editing) {
4870             return; // already editing..
4871         }
4872         if (is_edit) {
4873     
4874             this.headerEditEl.dom.value = this.title;
4875             this.headerEditEl.removeClass('d-none');
4876             this.headerEditEl.dom.focus();
4877             this.titleEl.addClass('d-none');
4878             
4879             this.is_header_editing = true;
4880             return
4881         }
4882         // flip back to not editing.
4883         this.title = this.headerEditEl.dom.value;
4884         this.headerEditEl.addClass('d-none');
4885         this.titleEl.removeClass('d-none');
4886         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4887         this.is_header_editing = false;
4888         this.fireEvent('titlechanged', this, this.title);
4889     
4890             
4891         
4892     }
4893
4894 });
4895
4896
4897 Roo.apply(Roo.bootstrap.Modal,  {
4898     /**
4899          * Button config that displays a single OK button
4900          * @type Object
4901          */
4902         OK :  [{
4903             name : 'ok',
4904             weight : 'primary',
4905             html : 'OK'
4906         }],
4907         /**
4908          * Button config that displays Yes and No buttons
4909          * @type Object
4910          */
4911         YESNO : [
4912             {
4913                 name  : 'no',
4914                 html : 'No'
4915             },
4916             {
4917                 name  :'yes',
4918                 weight : 'primary',
4919                 html : 'Yes'
4920             }
4921         ],
4922
4923         /**
4924          * Button config that displays OK and Cancel buttons
4925          * @type Object
4926          */
4927         OKCANCEL : [
4928             {
4929                name : 'cancel',
4930                 html : 'Cancel'
4931             },
4932             {
4933                 name : 'ok',
4934                 weight : 'primary',
4935                 html : 'OK'
4936             }
4937         ],
4938         /**
4939          * Button config that displays Yes, No and Cancel buttons
4940          * @type Object
4941          */
4942         YESNOCANCEL : [
4943             {
4944                 name : 'yes',
4945                 weight : 'primary',
4946                 html : 'Yes'
4947             },
4948             {
4949                 name : 'no',
4950                 html : 'No'
4951             },
4952             {
4953                 name : 'cancel',
4954                 html : 'Cancel'
4955             }
4956         ],
4957         
4958         zIndex : 10001
4959 });
4960
4961 /*
4962  * - LGPL
4963  *
4964  * messagebox - can be used as a replace
4965  * 
4966  */
4967 /**
4968  * @class Roo.MessageBox
4969  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4970  * Example usage:
4971  *<pre><code>
4972 // Basic alert:
4973 Roo.Msg.alert('Status', 'Changes saved successfully.');
4974
4975 // Prompt for user data:
4976 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4977     if (btn == 'ok'){
4978         // process text value...
4979     }
4980 });
4981
4982 // Show a dialog using config options:
4983 Roo.Msg.show({
4984    title:'Save Changes?',
4985    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4986    buttons: Roo.Msg.YESNOCANCEL,
4987    fn: processResult,
4988    animEl: 'elId'
4989 });
4990 </code></pre>
4991  * @static
4992  */
4993 Roo.bootstrap.MessageBox = function(){
4994     var dlg, opt, mask, waitTimer;
4995     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4996     var buttons, activeTextEl, bwidth;
4997
4998     
4999     // private
5000     var handleButton = function(button){
5001         dlg.hide();
5002         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5003     };
5004
5005     // private
5006     var handleHide = function(){
5007         if(opt && opt.cls){
5008             dlg.el.removeClass(opt.cls);
5009         }
5010         //if(waitTimer){
5011         //    Roo.TaskMgr.stop(waitTimer);
5012         //    waitTimer = null;
5013         //}
5014     };
5015
5016     // private
5017     var updateButtons = function(b){
5018         var width = 0;
5019         if(!b){
5020             buttons["ok"].hide();
5021             buttons["cancel"].hide();
5022             buttons["yes"].hide();
5023             buttons["no"].hide();
5024             dlg.footerEl.hide();
5025             
5026             return width;
5027         }
5028         dlg.footerEl.show();
5029         for(var k in buttons){
5030             if(typeof buttons[k] != "function"){
5031                 if(b[k]){
5032                     buttons[k].show();
5033                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5034                     width += buttons[k].el.getWidth()+15;
5035                 }else{
5036                     buttons[k].hide();
5037                 }
5038             }
5039         }
5040         return width;
5041     };
5042
5043     // private
5044     var handleEsc = function(d, k, e){
5045         if(opt && opt.closable !== false){
5046             dlg.hide();
5047         }
5048         if(e){
5049             e.stopEvent();
5050         }
5051     };
5052
5053     return {
5054         /**
5055          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5056          * @return {Roo.BasicDialog} The BasicDialog element
5057          */
5058         getDialog : function(){
5059            if(!dlg){
5060                 dlg = new Roo.bootstrap.Modal( {
5061                     //draggable: true,
5062                     //resizable:false,
5063                     //constraintoviewport:false,
5064                     //fixedcenter:true,
5065                     //collapsible : false,
5066                     //shim:true,
5067                     //modal: true,
5068                 //    width: 'auto',
5069                   //  height:100,
5070                     //buttonAlign:"center",
5071                     closeClick : function(){
5072                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5073                             handleButton("no");
5074                         }else{
5075                             handleButton("cancel");
5076                         }
5077                     }
5078                 });
5079                 dlg.render();
5080                 dlg.on("hide", handleHide);
5081                 mask = dlg.mask;
5082                 //dlg.addKeyListener(27, handleEsc);
5083                 buttons = {};
5084                 this.buttons = buttons;
5085                 var bt = this.buttonText;
5086                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5087                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5088                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5089                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5090                 //Roo.log(buttons);
5091                 bodyEl = dlg.bodyEl.createChild({
5092
5093                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5094                         '<textarea class="roo-mb-textarea"></textarea>' +
5095                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5096                 });
5097                 msgEl = bodyEl.dom.firstChild;
5098                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5099                 textboxEl.enableDisplayMode();
5100                 textboxEl.addKeyListener([10,13], function(){
5101                     if(dlg.isVisible() && opt && opt.buttons){
5102                         if(opt.buttons.ok){
5103                             handleButton("ok");
5104                         }else if(opt.buttons.yes){
5105                             handleButton("yes");
5106                         }
5107                     }
5108                 });
5109                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5110                 textareaEl.enableDisplayMode();
5111                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5112                 progressEl.enableDisplayMode();
5113                 
5114                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5115                 var pf = progressEl.dom.firstChild;
5116                 if (pf) {
5117                     pp = Roo.get(pf.firstChild);
5118                     pp.setHeight(pf.offsetHeight);
5119                 }
5120                 
5121             }
5122             return dlg;
5123         },
5124
5125         /**
5126          * Updates the message box body text
5127          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5128          * the XHTML-compliant non-breaking space character '&amp;#160;')
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateText : function(text)
5132         {
5133             if(!dlg.isVisible() && !opt.width){
5134                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5135                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5136             }
5137             msgEl.innerHTML = text || '&#160;';
5138       
5139             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5140             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5141             var w = Math.max(
5142                     Math.min(opt.width || cw , this.maxWidth), 
5143                     Math.max(opt.minWidth || this.minWidth, bwidth)
5144             );
5145             if(opt.prompt){
5146                 activeTextEl.setWidth(w);
5147             }
5148             if(dlg.isVisible()){
5149                 dlg.fixedcenter = false;
5150             }
5151             // to big, make it scroll. = But as usual stupid IE does not support
5152             // !important..
5153             
5154             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5155                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5156                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5157             } else {
5158                 bodyEl.dom.style.height = '';
5159                 bodyEl.dom.style.overflowY = '';
5160             }
5161             if (cw > w) {
5162                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5163             } else {
5164                 bodyEl.dom.style.overflowX = '';
5165             }
5166             
5167             dlg.setContentSize(w, bodyEl.getHeight());
5168             if(dlg.isVisible()){
5169                 dlg.fixedcenter = true;
5170             }
5171             return this;
5172         },
5173
5174         /**
5175          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5176          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5177          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5178          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5179          * @return {Roo.MessageBox} This message box
5180          */
5181         updateProgress : function(value, text){
5182             if(text){
5183                 this.updateText(text);
5184             }
5185             
5186             if (pp) { // weird bug on my firefox - for some reason this is not defined
5187                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5188                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5189             }
5190             return this;
5191         },        
5192
5193         /**
5194          * Returns true if the message box is currently displayed
5195          * @return {Boolean} True if the message box is visible, else false
5196          */
5197         isVisible : function(){
5198             return dlg && dlg.isVisible();  
5199         },
5200
5201         /**
5202          * Hides the message box if it is displayed
5203          */
5204         hide : function(){
5205             if(this.isVisible()){
5206                 dlg.hide();
5207             }  
5208         },
5209
5210         /**
5211          * Displays a new message box, or reinitializes an existing message box, based on the config options
5212          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5213          * The following config object properties are supported:
5214          * <pre>
5215 Property    Type             Description
5216 ----------  ---------------  ------------------------------------------------------------------------------------
5217 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5218                                    closes (defaults to undefined)
5219 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5220                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5221 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5222                                    progress and wait dialogs will ignore this property and always hide the
5223                                    close button as they can only be closed programmatically.
5224 cls               String           A custom CSS class to apply to the message box element
5225 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5226                                    displayed (defaults to 75)
5227 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5228                                    function will be btn (the name of the button that was clicked, if applicable,
5229                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5230                                    Progress and wait dialogs will ignore this option since they do not respond to
5231                                    user actions and can only be closed programmatically, so any required function
5232                                    should be called by the same code after it closes the dialog.
5233 icon              String           A CSS class that provides a background image to be used as an icon for
5234                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5235 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5236 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5237 modal             Boolean          False to allow user interaction with the page while the message box is
5238                                    displayed (defaults to true)
5239 msg               String           A string that will replace the existing message box body text (defaults
5240                                    to the XHTML-compliant non-breaking space character '&#160;')
5241 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5242 progress          Boolean          True to display a progress bar (defaults to false)
5243 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5244 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5245 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5246 title             String           The title text
5247 value             String           The string value to set into the active textbox element if displayed
5248 wait              Boolean          True to display a progress bar (defaults to false)
5249 width             Number           The width of the dialog in pixels
5250 </pre>
5251          *
5252          * Example usage:
5253          * <pre><code>
5254 Roo.Msg.show({
5255    title: 'Address',
5256    msg: 'Please enter your address:',
5257    width: 300,
5258    buttons: Roo.MessageBox.OKCANCEL,
5259    multiline: true,
5260    fn: saveAddress,
5261    animEl: 'addAddressBtn'
5262 });
5263 </code></pre>
5264          * @param {Object} config Configuration options
5265          * @return {Roo.MessageBox} This message box
5266          */
5267         show : function(options)
5268         {
5269             
5270             // this causes nightmares if you show one dialog after another
5271             // especially on callbacks..
5272              
5273             if(this.isVisible()){
5274                 
5275                 this.hide();
5276                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5277                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5278                 Roo.log("New Dialog Message:" +  options.msg )
5279                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5280                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5281                 
5282             }
5283             var d = this.getDialog();
5284             opt = options;
5285             d.setTitle(opt.title || "&#160;");
5286             d.closeEl.setDisplayed(opt.closable !== false);
5287             activeTextEl = textboxEl;
5288             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5289             if(opt.prompt){
5290                 if(opt.multiline){
5291                     textboxEl.hide();
5292                     textareaEl.show();
5293                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5294                         opt.multiline : this.defaultTextHeight);
5295                     activeTextEl = textareaEl;
5296                 }else{
5297                     textboxEl.show();
5298                     textareaEl.hide();
5299                 }
5300             }else{
5301                 textboxEl.hide();
5302                 textareaEl.hide();
5303             }
5304             progressEl.setDisplayed(opt.progress === true);
5305             if (opt.progress) {
5306                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5307             }
5308             this.updateProgress(0);
5309             activeTextEl.dom.value = opt.value || "";
5310             if(opt.prompt){
5311                 dlg.setDefaultButton(activeTextEl);
5312             }else{
5313                 var bs = opt.buttons;
5314                 var db = null;
5315                 if(bs && bs.ok){
5316                     db = buttons["ok"];
5317                 }else if(bs && bs.yes){
5318                     db = buttons["yes"];
5319                 }
5320                 dlg.setDefaultButton(db);
5321             }
5322             bwidth = updateButtons(opt.buttons);
5323             this.updateText(opt.msg);
5324             if(opt.cls){
5325                 d.el.addClass(opt.cls);
5326             }
5327             d.proxyDrag = opt.proxyDrag === true;
5328             d.modal = opt.modal !== false;
5329             d.mask = opt.modal !== false ? mask : false;
5330             if(!d.isVisible()){
5331                 // force it to the end of the z-index stack so it gets a cursor in FF
5332                 document.body.appendChild(dlg.el.dom);
5333                 d.animateTarget = null;
5334                 d.show(options.animEl);
5335             }
5336             return this;
5337         },
5338
5339         /**
5340          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5341          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5342          * and closing the message box when the process is complete.
5343          * @param {String} title The title bar text
5344          * @param {String} msg The message box body text
5345          * @return {Roo.MessageBox} This message box
5346          */
5347         progress : function(title, msg){
5348             this.show({
5349                 title : title,
5350                 msg : msg,
5351                 buttons: false,
5352                 progress:true,
5353                 closable:false,
5354                 minWidth: this.minProgressWidth,
5355                 modal : true
5356             });
5357             return this;
5358         },
5359
5360         /**
5361          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5362          * If a callback function is passed it will be called after the user clicks the button, and the
5363          * id of the button that was clicked will be passed as the only parameter to the callback
5364          * (could also be the top-right close button).
5365          * @param {String} title The title bar text
5366          * @param {String} msg The message box body text
5367          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5368          * @param {Object} scope (optional) The scope of the callback function
5369          * @return {Roo.MessageBox} This message box
5370          */
5371         alert : function(title, msg, fn, scope)
5372         {
5373             this.show({
5374                 title : title,
5375                 msg : msg,
5376                 buttons: this.OK,
5377                 fn: fn,
5378                 closable : false,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5387          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5388          * You are responsible for closing the message box when the process is complete.
5389          * @param {String} msg The message box body text
5390          * @param {String} title (optional) The title bar text
5391          * @return {Roo.MessageBox} This message box
5392          */
5393         wait : function(msg, title){
5394             this.show({
5395                 title : title,
5396                 msg : msg,
5397                 buttons: false,
5398                 closable:false,
5399                 progress:true,
5400                 modal:true,
5401                 width:300,
5402                 wait:true
5403             });
5404             waitTimer = Roo.TaskMgr.start({
5405                 run: function(i){
5406                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5407                 },
5408                 interval: 1000
5409             });
5410             return this;
5411         },
5412
5413         /**
5414          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5415          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5416          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5417          * @param {String} title The title bar text
5418          * @param {String} msg The message box body text
5419          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5420          * @param {Object} scope (optional) The scope of the callback function
5421          * @return {Roo.MessageBox} This message box
5422          */
5423         confirm : function(title, msg, fn, scope){
5424             this.show({
5425                 title : title,
5426                 msg : msg,
5427                 buttons: this.YESNO,
5428                 fn: fn,
5429                 scope : scope,
5430                 modal : true
5431             });
5432             return this;
5433         },
5434
5435         /**
5436          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5437          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5438          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5439          * (could also be the top-right close button) and the text that was entered will be passed as the two
5440          * parameters to the callback.
5441          * @param {String} title The title bar text
5442          * @param {String} msg The message box body text
5443          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5444          * @param {Object} scope (optional) The scope of the callback function
5445          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5446          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5447          * @return {Roo.MessageBox} This message box
5448          */
5449         prompt : function(title, msg, fn, scope, multiline){
5450             this.show({
5451                 title : title,
5452                 msg : msg,
5453                 buttons: this.OKCANCEL,
5454                 fn: fn,
5455                 minWidth:250,
5456                 scope : scope,
5457                 prompt:true,
5458                 multiline: multiline,
5459                 modal : true
5460             });
5461             return this;
5462         },
5463
5464         /**
5465          * Button config that displays a single OK button
5466          * @type Object
5467          */
5468         OK : {ok:true},
5469         /**
5470          * Button config that displays Yes and No buttons
5471          * @type Object
5472          */
5473         YESNO : {yes:true, no:true},
5474         /**
5475          * Button config that displays OK and Cancel buttons
5476          * @type Object
5477          */
5478         OKCANCEL : {ok:true, cancel:true},
5479         /**
5480          * Button config that displays Yes, No and Cancel buttons
5481          * @type Object
5482          */
5483         YESNOCANCEL : {yes:true, no:true, cancel:true},
5484
5485         /**
5486          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5487          * @type Number
5488          */
5489         defaultTextHeight : 75,
5490         /**
5491          * The maximum width in pixels of the message box (defaults to 600)
5492          * @type Number
5493          */
5494         maxWidth : 600,
5495         /**
5496          * The minimum width in pixels of the message box (defaults to 100)
5497          * @type Number
5498          */
5499         minWidth : 100,
5500         /**
5501          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5502          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5503          * @type Number
5504          */
5505         minProgressWidth : 250,
5506         /**
5507          * An object containing the default button text strings that can be overriden for localized language support.
5508          * Supported properties are: ok, cancel, yes and no.
5509          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5510          * @type Object
5511          */
5512         buttonText : {
5513             ok : "OK",
5514             cancel : "Cancel",
5515             yes : "Yes",
5516             no : "No"
5517         }
5518     };
5519 }();
5520
5521 /**
5522  * Shorthand for {@link Roo.MessageBox}
5523  */
5524 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5525 Roo.Msg = Roo.Msg || Roo.MessageBox;
5526 /*
5527  * - LGPL
5528  *
5529  * navbar
5530  * 
5531  */
5532
5533 /**
5534  * @class Roo.bootstrap.nav.Bar
5535  * @extends Roo.bootstrap.Component
5536  * @abstract
5537  * Bootstrap Navbar class
5538
5539  * @constructor
5540  * Create a new Navbar
5541  * @param {Object} config The config object
5542  */
5543
5544
5545 Roo.bootstrap.nav.Bar = function(config){
5546     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5547     this.addEvents({
5548         // raw events
5549         /**
5550          * @event beforetoggle
5551          * Fire before toggle the menu
5552          * @param {Roo.EventObject} e
5553          */
5554         "beforetoggle" : true
5555     });
5556 };
5557
5558 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5559     
5560     
5561    
5562     // private
5563     navItems : false,
5564     loadMask : false,
5565     
5566     
5567     getAutoCreate : function(){
5568         
5569         
5570         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5571         
5572     },
5573     
5574     initEvents :function ()
5575     {
5576         //Roo.log(this.el.select('.navbar-toggle',true));
5577         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5578         
5579         var mark = {
5580             tag: "div",
5581             cls:"x-dlg-mask"
5582         };
5583         
5584         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5585         
5586         var size = this.el.getSize();
5587         this.maskEl.setSize(size.width, size.height);
5588         this.maskEl.enableDisplayMode("block");
5589         this.maskEl.hide();
5590         
5591         if(this.loadMask){
5592             this.maskEl.show();
5593         }
5594     },
5595     
5596     
5597     getChildContainer : function()
5598     {
5599         if (this.el && this.el.select('.collapse').getCount()) {
5600             return this.el.select('.collapse',true).first();
5601         }
5602         
5603         return this.el;
5604     },
5605     
5606     mask : function()
5607     {
5608         this.maskEl.show();
5609     },
5610     
5611     unmask : function()
5612     {
5613         this.maskEl.hide();
5614     },
5615     onToggle : function()
5616     {
5617         
5618         if(this.fireEvent('beforetoggle', this) === false){
5619             return;
5620         }
5621         var ce = this.el.select('.navbar-collapse',true).first();
5622       
5623         if (!ce.hasClass('show')) {
5624            this.expand();
5625         } else {
5626             this.collapse();
5627         }
5628         
5629         
5630     
5631     },
5632     /**
5633      * Expand the navbar pulldown 
5634      */
5635     expand : function ()
5636     {
5637        
5638         var ce = this.el.select('.navbar-collapse',true).first();
5639         if (ce.hasClass('collapsing')) {
5640             return;
5641         }
5642         ce.dom.style.height = '';
5643                // show it...
5644         ce.addClass('in'); // old...
5645         ce.removeClass('collapse');
5646         ce.addClass('show');
5647         var h = ce.getHeight();
5648         Roo.log(h);
5649         ce.removeClass('show');
5650         // at this point we should be able to see it..
5651         ce.addClass('collapsing');
5652         
5653         ce.setHeight(0); // resize it ...
5654         ce.on('transitionend', function() {
5655             //Roo.log('done transition');
5656             ce.removeClass('collapsing');
5657             ce.addClass('show');
5658             ce.removeClass('collapse');
5659
5660             ce.dom.style.height = '';
5661         }, this, { single: true} );
5662         ce.setHeight(h);
5663         ce.dom.scrollTop = 0;
5664     },
5665     /**
5666      * Collapse the navbar pulldown 
5667      */
5668     collapse : function()
5669     {
5670          var ce = this.el.select('.navbar-collapse',true).first();
5671        
5672         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5673             // it's collapsed or collapsing..
5674             return;
5675         }
5676         ce.removeClass('in'); // old...
5677         ce.setHeight(ce.getHeight());
5678         ce.removeClass('show');
5679         ce.addClass('collapsing');
5680         
5681         ce.on('transitionend', function() {
5682             ce.dom.style.height = '';
5683             ce.removeClass('collapsing');
5684             ce.addClass('collapse');
5685         }, this, { single: true} );
5686         ce.setHeight(0);
5687     }
5688     
5689     
5690     
5691 });
5692
5693
5694
5695  
5696
5697  /*
5698  * - LGPL
5699  *
5700  * navbar
5701  * 
5702  */
5703
5704 /**
5705  * @class Roo.bootstrap.nav.Simplebar
5706  * @extends Roo.bootstrap.nav.Bar
5707  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5708  * Bootstrap Sidebar class
5709  *
5710  * @cfg {Boolean} inverse is inverted color
5711  * 
5712  * @cfg {String} type (nav | pills | tabs)
5713  * @cfg {Boolean} arrangement stacked | justified
5714  * @cfg {String} align (left | right) alignment
5715  * 
5716  * @cfg {Boolean} main (true|false) main nav bar? default false
5717  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5718  * 
5719  * @cfg {String} tag (header|footer|nav|div) default is nav 
5720
5721  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5722  * 
5723  * 
5724  * @constructor
5725  * Create a new Sidebar
5726  * @param {Object} config The config object
5727  */
5728
5729
5730 Roo.bootstrap.nav.Simplebar = function(config){
5731     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5732 };
5733
5734 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5735     
5736     inverse: false,
5737     
5738     type: false,
5739     arrangement: '',
5740     align : false,
5741     
5742     weight : 'light',
5743     
5744     main : false,
5745     
5746     
5747     tag : false,
5748     
5749     
5750     getAutoCreate : function(){
5751         
5752         
5753         var cfg = {
5754             tag : this.tag || 'div',
5755             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5756         };
5757         if (['light','white'].indexOf(this.weight) > -1) {
5758             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5759         }
5760         cfg.cls += ' bg-' + this.weight;
5761         
5762         if (this.inverse) {
5763             cfg.cls += ' navbar-inverse';
5764             
5765         }
5766         
5767         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5768         
5769         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5770             return cfg;
5771         }
5772         
5773         
5774     
5775         
5776         cfg.cn = [
5777             {
5778                 cls: 'nav nav-' + this.xtype,
5779                 tag : 'ul'
5780             }
5781         ];
5782         
5783          
5784         this.type = this.type || 'nav';
5785         if (['tabs','pills'].indexOf(this.type) != -1) {
5786             cfg.cn[0].cls += ' nav-' + this.type
5787         
5788         
5789         } else {
5790             if (this.type!=='nav') {
5791                 Roo.log('nav type must be nav/tabs/pills')
5792             }
5793             cfg.cn[0].cls += ' navbar-nav'
5794         }
5795         
5796         
5797         
5798         
5799         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5800             cfg.cn[0].cls += ' nav-' + this.arrangement;
5801         }
5802         
5803         
5804         if (this.align === 'right') {
5805             cfg.cn[0].cls += ' navbar-right';
5806         }
5807         
5808         
5809         
5810         
5811         return cfg;
5812     
5813         
5814     }
5815     
5816     
5817     
5818 });
5819
5820
5821
5822  
5823
5824  
5825        /*
5826  * - LGPL
5827  *
5828  * navbar
5829  * navbar-fixed-top
5830  * navbar-expand-md  fixed-top 
5831  */
5832
5833 /**
5834  * @class Roo.bootstrap.nav.Headerbar
5835  * @extends Roo.bootstrap.nav.Simplebar
5836  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5837  * Bootstrap Sidebar class
5838  *
5839  * @cfg {String} brand what is brand
5840  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5841  * @cfg {String} brand_href href of the brand
5842  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5843  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5844  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5845  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5846  * 
5847  * @constructor
5848  * Create a new Sidebar
5849  * @param {Object} config The config object
5850  */
5851
5852
5853 Roo.bootstrap.nav.Headerbar = function(config){
5854     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5855       
5856 };
5857
5858 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5859     
5860     position: '',
5861     brand: '',
5862     brand_href: false,
5863     srButton : true,
5864     autohide : false,
5865     desktopCenter : false,
5866    
5867     
5868     getAutoCreate : function(){
5869         
5870         var   cfg = {
5871             tag: this.nav || 'nav',
5872             cls: 'navbar navbar-expand-md',
5873             role: 'navigation',
5874             cn: []
5875         };
5876         
5877         var cn = cfg.cn;
5878         if (this.desktopCenter) {
5879             cn.push({cls : 'container', cn : []});
5880             cn = cn[0].cn;
5881         }
5882         
5883         if(this.srButton){
5884             var btn = {
5885                 tag: 'button',
5886                 type: 'button',
5887                 cls: 'navbar-toggle navbar-toggler',
5888                 'data-toggle': 'collapse',
5889                 cn: [
5890                     {
5891                         tag: 'span',
5892                         cls: 'sr-only',
5893                         html: 'Toggle navigation'
5894                     },
5895                     {
5896                         tag: 'span',
5897                         cls: 'icon-bar navbar-toggler-icon'
5898                     },
5899                     {
5900                         tag: 'span',
5901                         cls: 'icon-bar'
5902                     },
5903                     {
5904                         tag: 'span',
5905                         cls: 'icon-bar'
5906                     }
5907                 ]
5908             };
5909             
5910             cn.push( Roo.bootstrap.version == 4 ? btn : {
5911                 tag: 'div',
5912                 cls: 'navbar-header',
5913                 cn: [
5914                     btn
5915                 ]
5916             });
5917         }
5918         
5919         cn.push({
5920             tag: 'div',
5921             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5922             cn : []
5923         });
5924         
5925         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5926         
5927         if (['light','white'].indexOf(this.weight) > -1) {
5928             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5929         }
5930         cfg.cls += ' bg-' + this.weight;
5931         
5932         
5933         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5934             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5935             
5936             // tag can override this..
5937             
5938             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5939         }
5940         
5941         if (this.brand !== '') {
5942             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5943             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5944                 tag: 'a',
5945                 href: this.brand_href ? this.brand_href : '#',
5946                 cls: 'navbar-brand',
5947                 cn: [
5948                 this.brand
5949                 ]
5950             });
5951         }
5952         
5953         if(this.main){
5954             cfg.cls += ' main-nav';
5955         }
5956         
5957         
5958         return cfg;
5959
5960         
5961     },
5962     getHeaderChildContainer : function()
5963     {
5964         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5965             return this.el.select('.navbar-header',true).first();
5966         }
5967         
5968         return this.getChildContainer();
5969     },
5970     
5971     getChildContainer : function()
5972     {
5973          
5974         return this.el.select('.roo-navbar-collapse',true).first();
5975          
5976         
5977     },
5978     
5979     initEvents : function()
5980     {
5981         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5982         
5983         if (this.autohide) {
5984             
5985             var prevScroll = 0;
5986             var ft = this.el;
5987             
5988             Roo.get(document).on('scroll',function(e) {
5989                 var ns = Roo.get(document).getScroll().top;
5990                 var os = prevScroll;
5991                 prevScroll = ns;
5992                 
5993                 if(ns > os){
5994                     ft.removeClass('slideDown');
5995                     ft.addClass('slideUp');
5996                     return;
5997                 }
5998                 ft.removeClass('slideUp');
5999                 ft.addClass('slideDown');
6000                  
6001               
6002           },this);
6003         }
6004     }    
6005     
6006 });
6007
6008
6009
6010  
6011
6012  /*
6013  * - LGPL
6014  *
6015  * navbar
6016  * 
6017  */
6018
6019 /**
6020  * @class Roo.bootstrap.nav.Sidebar
6021  * @extends Roo.bootstrap.nav.Bar
6022  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6023  * Bootstrap Sidebar class
6024  * 
6025  * @constructor
6026  * Create a new Sidebar
6027  * @param {Object} config The config object
6028  */
6029
6030
6031 Roo.bootstrap.nav.Sidebar = function(config){
6032     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6033 };
6034
6035 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6036     
6037     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6038     
6039     getAutoCreate : function(){
6040         
6041         
6042         return  {
6043             tag: 'div',
6044             cls: 'sidebar sidebar-nav'
6045         };
6046     
6047         
6048     }
6049     
6050     
6051     
6052 });
6053
6054
6055
6056  
6057
6058  /*
6059  * - LGPL
6060  *
6061  * nav group
6062  * 
6063  */
6064
6065 /**
6066  * @class Roo.bootstrap.nav.Group
6067  * @extends Roo.bootstrap.Component
6068  * @children Roo.bootstrap.nav.Item
6069  * Bootstrap NavGroup class
6070  * @cfg {String} align (left|right)
6071  * @cfg {Boolean} inverse
6072  * @cfg {String} type (nav|pills|tab) default nav
6073  * @cfg {String} navId - reference Id for navbar.
6074  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6075  * 
6076  * @constructor
6077  * Create a new nav group
6078  * @param {Object} config The config object
6079  */
6080
6081 Roo.bootstrap.nav.Group = function(config){
6082     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6083     this.navItems = [];
6084    
6085     Roo.bootstrap.nav.Group.register(this);
6086      this.addEvents({
6087         /**
6088              * @event changed
6089              * Fires when the active item changes
6090              * @param {Roo.bootstrap.nav.Group} this
6091              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6092              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6093          */
6094         'changed': true
6095      });
6096     
6097 };
6098
6099 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6100     
6101     align: '',
6102     inverse: false,
6103     form: false,
6104     type: 'nav',
6105     navId : '',
6106     // private
6107     pilltype : true,
6108     
6109     navItems : false, 
6110     
6111     getAutoCreate : function()
6112     {
6113         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6114         
6115         cfg = {
6116             tag : 'ul',
6117             cls: 'nav' 
6118         };
6119         if (Roo.bootstrap.version == 4) {
6120             if (['tabs','pills'].indexOf(this.type) != -1) {
6121                 cfg.cls += ' nav-' + this.type; 
6122             } else {
6123                 // trying to remove so header bar can right align top?
6124                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6125                     // do not use on header bar... 
6126                     cfg.cls += ' navbar-nav';
6127                 }
6128             }
6129             
6130         } else {
6131             if (['tabs','pills'].indexOf(this.type) != -1) {
6132                 cfg.cls += ' nav-' + this.type
6133             } else {
6134                 if (this.type !== 'nav') {
6135                     Roo.log('nav type must be nav/tabs/pills')
6136                 }
6137                 cfg.cls += ' navbar-nav'
6138             }
6139         }
6140         
6141         if (this.parent() && this.parent().sidebar) {
6142             cfg = {
6143                 tag: 'ul',
6144                 cls: 'dashboard-menu sidebar-menu'
6145             };
6146             
6147             return cfg;
6148         }
6149         
6150         if (this.form === true) {
6151             cfg = {
6152                 tag: 'form',
6153                 cls: 'navbar-form form-inline'
6154             };
6155             //nav navbar-right ml-md-auto
6156             if (this.align === 'right') {
6157                 cfg.cls += ' navbar-right ml-md-auto';
6158             } else {
6159                 cfg.cls += ' navbar-left';
6160             }
6161         }
6162         
6163         if (this.align === 'right') {
6164             cfg.cls += ' navbar-right ml-md-auto';
6165         } else {
6166             cfg.cls += ' mr-auto';
6167         }
6168         
6169         if (this.inverse) {
6170             cfg.cls += ' navbar-inverse';
6171             
6172         }
6173         
6174         
6175         return cfg;
6176     },
6177     /**
6178     * sets the active Navigation item
6179     * @param {Roo.bootstrap.nav.Item} the new current navitem
6180     */
6181     setActiveItem : function(item)
6182     {
6183         var prev = false;
6184         Roo.each(this.navItems, function(v){
6185             if (v == item) {
6186                 return ;
6187             }
6188             if (v.isActive()) {
6189                 v.setActive(false, true);
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195
6196         item.setActive(true, true);
6197         this.fireEvent('changed', this, item, prev);
6198         
6199         
6200     },
6201     /**
6202     * gets the active Navigation item
6203     * @return {Roo.bootstrap.nav.Item} the current navitem
6204     */
6205     getActive : function()
6206     {
6207         
6208         var prev = false;
6209         Roo.each(this.navItems, function(v){
6210             
6211             if (v.isActive()) {
6212                 prev = v;
6213                 
6214             }
6215             
6216         });
6217         return prev;
6218     },
6219     
6220     indexOfNav : function()
6221     {
6222         
6223         var prev = false;
6224         Roo.each(this.navItems, function(v,i){
6225             
6226             if (v.isActive()) {
6227                 prev = i;
6228                 
6229             }
6230             
6231         });
6232         return prev;
6233     },
6234     /**
6235     * adds a Navigation item
6236     * @param {Roo.bootstrap.nav.Item} the navitem to add
6237     */
6238     addItem : function(cfg)
6239     {
6240         if (this.form && Roo.bootstrap.version == 4) {
6241             cfg.tag = 'div';
6242         }
6243         var cn = new Roo.bootstrap.nav.Item(cfg);
6244         this.register(cn);
6245         cn.parentId = this.id;
6246         cn.onRender(this.el, null);
6247         return cn;
6248     },
6249     /**
6250     * register a Navigation item
6251     * @param {Roo.bootstrap.nav.Item} the navitem to add
6252     */
6253     register : function(item)
6254     {
6255         this.navItems.push( item);
6256         item.navId = this.navId;
6257     
6258     },
6259     
6260     /**
6261     * clear all the Navigation item
6262     */
6263    
6264     clearAll : function()
6265     {
6266         this.navItems = [];
6267         this.el.dom.innerHTML = '';
6268     },
6269     
6270     getNavItem: function(tabId)
6271     {
6272         var ret = false;
6273         Roo.each(this.navItems, function(e) {
6274             if (e.tabId == tabId) {
6275                ret =  e;
6276                return false;
6277             }
6278             return true;
6279             
6280         });
6281         return ret;
6282     },
6283     
6284     setActiveNext : function()
6285     {
6286         var i = this.indexOfNav(this.getActive());
6287         if (i > this.navItems.length) {
6288             return;
6289         }
6290         this.setActiveItem(this.navItems[i+1]);
6291     },
6292     setActivePrev : function()
6293     {
6294         var i = this.indexOfNav(this.getActive());
6295         if (i  < 1) {
6296             return;
6297         }
6298         this.setActiveItem(this.navItems[i-1]);
6299     },
6300     clearWasActive : function(except) {
6301         Roo.each(this.navItems, function(e) {
6302             if (e.tabId != except.tabId && e.was_active) {
6303                e.was_active = false;
6304                return false;
6305             }
6306             return true;
6307             
6308         });
6309     },
6310     getWasActive : function ()
6311     {
6312         var r = false;
6313         Roo.each(this.navItems, function(e) {
6314             if (e.was_active) {
6315                r = e;
6316                return false;
6317             }
6318             return true;
6319             
6320         });
6321         return r;
6322     }
6323     
6324     
6325 });
6326
6327  
6328 Roo.apply(Roo.bootstrap.nav.Group, {
6329     
6330     groups: {},
6331      /**
6332     * register a Navigation Group
6333     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6334     */
6335     register : function(navgrp)
6336     {
6337         this.groups[navgrp.navId] = navgrp;
6338         
6339     },
6340     /**
6341     * fetch a Navigation Group based on the navigation ID
6342     * @param {string} the navgroup to add
6343     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6344     */
6345     get: function(navId) {
6346         if (typeof(this.groups[navId]) == 'undefined') {
6347             return false;
6348             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6349         }
6350         return this.groups[navId] ;
6351     }
6352     
6353     
6354     
6355 });
6356
6357  /**
6358  * @class Roo.bootstrap.nav.Item
6359  * @extends Roo.bootstrap.Component
6360  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6361  * @parent Roo.bootstrap.nav.Group
6362  * @licence LGPL
6363  * Bootstrap Navbar.NavItem class
6364  * 
6365  * @cfg {String} href  link to
6366  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6367  * @cfg {Boolean} button_outline show and outlined button
6368  * @cfg {String} html content of button
6369  * @cfg {String} badge text inside badge
6370  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6371  * @cfg {String} glyphicon DEPRICATED - use fa
6372  * @cfg {String} icon DEPRICATED - use fa
6373  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6374  * @cfg {Boolean} active Is item active
6375  * @cfg {Boolean} disabled Is item disabled
6376  * @cfg {String} linkcls  Link Class
6377  * @cfg {Boolean} preventDefault (true | false) default false
6378  * @cfg {String} tabId the tab that this item activates.
6379  * @cfg {String} tagtype (a|span) render as a href or span?
6380  * @cfg {Boolean} animateRef (true|false) link to element default false  
6381  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6382   
6383  * @constructor
6384  * Create a new Navbar Item
6385  * @param {Object} config The config object
6386  */
6387 Roo.bootstrap.nav.Item = function(config){
6388     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6389     this.addEvents({
6390         // raw events
6391         /**
6392          * @event click
6393          * The raw click event for the entire grid.
6394          * @param {Roo.EventObject} e
6395          */
6396         "click" : true,
6397          /**
6398             * @event changed
6399             * Fires when the active item active state changes
6400             * @param {Roo.bootstrap.nav.Item} this
6401             * @param {boolean} state the new state
6402              
6403          */
6404         'changed': true,
6405         /**
6406             * @event scrollto
6407             * Fires when scroll to element
6408             * @param {Roo.bootstrap.nav.Item} this
6409             * @param {Object} options
6410             * @param {Roo.EventObject} e
6411              
6412          */
6413         'scrollto': true
6414     });
6415    
6416 };
6417
6418 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6419     
6420     href: false,
6421     html: '',
6422     badge: '',
6423     icon: false,
6424     fa : false,
6425     glyphicon: false,
6426     active: false,
6427     preventDefault : false,
6428     tabId : false,
6429     tagtype : 'a',
6430     tag: 'li',
6431     disabled : false,
6432     animateRef : false,
6433     was_active : false,
6434     button_weight : '',
6435     button_outline : false,
6436     linkcls : '',
6437     navLink: false,
6438     
6439     getAutoCreate : function(){
6440          
6441         var cfg = {
6442             tag: this.tag,
6443             cls: 'nav-item'
6444         };
6445         
6446         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6447         
6448         if (this.active) {
6449             cfg.cls +=  ' active' ;
6450         }
6451         if (this.disabled) {
6452             cfg.cls += ' disabled';
6453         }
6454         
6455         // BS4 only?
6456         if (this.button_weight.length) {
6457             cfg.tag = this.href ? 'a' : 'button';
6458             cfg.html = this.html || '';
6459             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6460             if (this.href) {
6461                 cfg.href = this.href;
6462             }
6463             if (this.fa) {
6464                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6465             } else {
6466                 cfg.cls += " nav-html";
6467             }
6468             
6469             // menu .. should add dropdown-menu class - so no need for carat..
6470             
6471             if (this.badge !== '') {
6472                  
6473                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6474             }
6475             return cfg;
6476         }
6477         
6478         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6479             cfg.cn = [
6480                 {
6481                     tag: this.tagtype,
6482                     href : this.href || "#",
6483                     html: this.html || '',
6484                     cls : ''
6485                 }
6486             ];
6487             if (this.tagtype == 'a') {
6488                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6489         
6490             }
6491             if (this.icon) {
6492                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6493             } else  if (this.fa) {
6494                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495             } else if(this.glyphicon) {
6496                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6497             } else {
6498                 cfg.cn[0].cls += " nav-html";
6499             }
6500             
6501             if (this.menu) {
6502                 cfg.cn[0].html += " <span class='caret'></span>";
6503              
6504             }
6505             
6506             if (this.badge !== '') {
6507                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6508             }
6509         }
6510         
6511         
6512         
6513         return cfg;
6514     },
6515     onRender : function(ct, position)
6516     {
6517        // Roo.log("Call onRender: " + this.xtype);
6518         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6519             this.tag = 'div';
6520         }
6521         
6522         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6523         this.navLink = this.el.select('.nav-link',true).first();
6524         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6525         return ret;
6526     },
6527       
6528     
6529     initEvents: function() 
6530     {
6531         if (typeof (this.menu) != 'undefined') {
6532             this.menu.parentType = this.xtype;
6533             this.menu.triggerEl = this.el;
6534             this.menu = this.addxtype(Roo.apply({}, this.menu));
6535         }
6536         
6537         this.el.on('click', this.onClick, this);
6538         
6539         //if(this.tagtype == 'span'){
6540         //    this.el.select('span',true).on('click', this.onClick, this);
6541         //}
6542        
6543         // at this point parent should be available..
6544         this.parent().register(this);
6545     },
6546     
6547     onClick : function(e)
6548     {
6549         if (e.getTarget('.dropdown-menu-item')) {
6550             // did you click on a menu itemm.... - then don't trigger onclick..
6551             return;
6552         }
6553         
6554         if(
6555                 this.preventDefault ||
6556                                 this.href === false ||
6557                 this.href === '#' 
6558         ){
6559             //Roo.log("NavItem - prevent Default?");
6560             e.preventDefault();
6561         }
6562         
6563         if (this.disabled) {
6564             return;
6565         }
6566         
6567         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6568         if (tg && tg.transition) {
6569             Roo.log("waiting for the transitionend");
6570             return;
6571         }
6572         
6573         
6574         
6575         //Roo.log("fire event clicked");
6576         if(this.fireEvent('click', this, e) === false){
6577             return;
6578         };
6579         
6580         if(this.tagtype == 'span'){
6581             return;
6582         }
6583         
6584         //Roo.log(this.href);
6585         var ael = this.el.select('a',true).first();
6586         //Roo.log(ael);
6587         
6588         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6589             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6590             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6591                 return; // ignore... - it's a 'hash' to another page.
6592             }
6593             Roo.log("NavItem - prevent Default?");
6594             e.preventDefault();
6595             this.scrollToElement(e);
6596         }
6597         
6598         
6599         var p =  this.parent();
6600    
6601         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6602             if (typeof(p.setActiveItem) !== 'undefined') {
6603                 p.setActiveItem(this);
6604             }
6605         }
6606         
6607         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6608         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6609             // remove the collapsed menu expand...
6610             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6611         }
6612     },
6613     
6614     isActive: function () {
6615         return this.active
6616     },
6617     setActive : function(state, fire, is_was_active)
6618     {
6619         if (this.active && !state && this.navId) {
6620             this.was_active = true;
6621             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6622             if (nv) {
6623                 nv.clearWasActive(this);
6624             }
6625             
6626         }
6627         this.active = state;
6628         
6629         if (!state ) {
6630             this.el.removeClass('active');
6631             this.navLink ? this.navLink.removeClass('active') : false;
6632         } else if (!this.el.hasClass('active')) {
6633             
6634             this.el.addClass('active');
6635             if (Roo.bootstrap.version == 4 && this.navLink ) {
6636                 this.navLink.addClass('active');
6637             }
6638             
6639         }
6640         if (fire) {
6641             this.fireEvent('changed', this, state);
6642         }
6643         
6644         // show a panel if it's registered and related..
6645         
6646         if (!this.navId || !this.tabId || !state || is_was_active) {
6647             return;
6648         }
6649         
6650         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6651         if (!tg) {
6652             return;
6653         }
6654         var pan = tg.getPanelByName(this.tabId);
6655         if (!pan) {
6656             return;
6657         }
6658         // if we can not flip to new panel - go back to old nav highlight..
6659         if (false == tg.showPanel(pan)) {
6660             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6661             if (nv) {
6662                 var onav = nv.getWasActive();
6663                 if (onav) {
6664                     onav.setActive(true, false, true);
6665                 }
6666             }
6667             
6668         }
6669         
6670         
6671         
6672     },
6673      // this should not be here...
6674     setDisabled : function(state)
6675     {
6676         this.disabled = state;
6677         if (!state ) {
6678             this.el.removeClass('disabled');
6679         } else if (!this.el.hasClass('disabled')) {
6680             this.el.addClass('disabled');
6681         }
6682         
6683     },
6684     
6685     /**
6686      * Fetch the element to display the tooltip on.
6687      * @return {Roo.Element} defaults to this.el
6688      */
6689     tooltipEl : function()
6690     {
6691         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6692     },
6693     
6694     scrollToElement : function(e)
6695     {
6696         var c = document.body;
6697         
6698         /*
6699          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6700          */
6701         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6702             c = document.documentElement;
6703         }
6704         
6705         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6706         
6707         if(!target){
6708             return;
6709         }
6710
6711         var o = target.calcOffsetsTo(c);
6712         
6713         var options = {
6714             target : target,
6715             value : o[1]
6716         };
6717         
6718         this.fireEvent('scrollto', this, options, e);
6719         
6720         Roo.get(c).scrollTo('top', options.value, true);
6721         
6722         return;
6723     },
6724     /**
6725      * Set the HTML (text content) of the item
6726      * @param {string} html  content for the nav item
6727      */
6728     setHtml : function(html)
6729     {
6730         this.html = html;
6731         this.htmlEl.dom.innerHTML = html;
6732         
6733     } 
6734 });
6735  
6736
6737  /*
6738  * - LGPL
6739  *
6740  * sidebar item
6741  *
6742  *  li
6743  *    <span> icon </span>
6744  *    <span> text </span>
6745  *    <span>badge </span>
6746  */
6747
6748 /**
6749  * @class Roo.bootstrap.nav.SidebarItem
6750  * @extends Roo.bootstrap.nav.Item
6751  * Bootstrap Navbar.NavSidebarItem class
6752  * 
6753  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6754  * {Boolean} open is the menu open
6755  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6756  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6757  * {String} buttonSize (sm|md|lg)the extra classes for the button
6758  * {Boolean} showArrow show arrow next to the text (default true)
6759  * @constructor
6760  * Create a new Navbar Button
6761  * @param {Object} config The config object
6762  */
6763 Roo.bootstrap.nav.SidebarItem = function(config){
6764     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6765     this.addEvents({
6766         // raw events
6767         /**
6768          * @event click
6769          * The raw click event for the entire grid.
6770          * @param {Roo.EventObject} e
6771          */
6772         "click" : true,
6773          /**
6774             * @event changed
6775             * Fires when the active item active state changes
6776             * @param {Roo.bootstrap.nav.SidebarItem} this
6777             * @param {boolean} state the new state
6778              
6779          */
6780         'changed': true
6781     });
6782    
6783 };
6784
6785 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6786     
6787     badgeWeight : 'default',
6788     
6789     open: false,
6790     
6791     buttonView : false,
6792     
6793     buttonWeight : 'default',
6794     
6795     buttonSize : 'md',
6796     
6797     showArrow : true,
6798     
6799     getAutoCreate : function(){
6800         
6801         
6802         var a = {
6803                 tag: 'a',
6804                 href : this.href || '#',
6805                 cls: '',
6806                 html : '',
6807                 cn : []
6808         };
6809         
6810         if(this.buttonView){
6811             a = {
6812                 tag: 'button',
6813                 href : this.href || '#',
6814                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6815                 html : this.html,
6816                 cn : []
6817             };
6818         }
6819         
6820         var cfg = {
6821             tag: 'li',
6822             cls: '',
6823             cn: [ a ]
6824         };
6825         
6826         if (this.active) {
6827             cfg.cls += ' active';
6828         }
6829         
6830         if (this.disabled) {
6831             cfg.cls += ' disabled';
6832         }
6833         if (this.open) {
6834             cfg.cls += ' open x-open';
6835         }
6836         // left icon..
6837         if (this.glyphicon || this.icon) {
6838             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6839             a.cn.push({ tag : 'i', cls : c }) ;
6840         }
6841         
6842         if(!this.buttonView){
6843             var span = {
6844                 tag: 'span',
6845                 html : this.html || ''
6846             };
6847
6848             a.cn.push(span);
6849             
6850         }
6851         
6852         if (this.badge !== '') {
6853             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6854         }
6855         
6856         if (this.menu) {
6857             
6858             if(this.showArrow){
6859                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6860             }
6861             
6862             a.cls += ' dropdown-toggle treeview' ;
6863         }
6864         
6865         return cfg;
6866     },
6867     
6868     initEvents : function()
6869     { 
6870         if (typeof (this.menu) != 'undefined') {
6871             this.menu.parentType = this.xtype;
6872             this.menu.triggerEl = this.el;
6873             this.menu = this.addxtype(Roo.apply({}, this.menu));
6874         }
6875         
6876         this.el.on('click', this.onClick, this);
6877         
6878         if(this.badge !== ''){
6879             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6880         }
6881         
6882     },
6883     
6884     onClick : function(e)
6885     {
6886         if(this.disabled){
6887             e.preventDefault();
6888             return;
6889         }
6890         
6891         if(this.preventDefault){
6892             e.preventDefault();
6893         }
6894         
6895         this.fireEvent('click', this, e);
6896     },
6897     
6898     disable : function()
6899     {
6900         this.setDisabled(true);
6901     },
6902     
6903     enable : function()
6904     {
6905         this.setDisabled(false);
6906     },
6907     
6908     setDisabled : function(state)
6909     {
6910         if(this.disabled == state){
6911             return;
6912         }
6913         
6914         this.disabled = state;
6915         
6916         if (state) {
6917             this.el.addClass('disabled');
6918             return;
6919         }
6920         
6921         this.el.removeClass('disabled');
6922         
6923         return;
6924     },
6925     
6926     setActive : function(state)
6927     {
6928         if(this.active == state){
6929             return;
6930         }
6931         
6932         this.active = state;
6933         
6934         if (state) {
6935             this.el.addClass('active');
6936             return;
6937         }
6938         
6939         this.el.removeClass('active');
6940         
6941         return;
6942     },
6943     
6944     isActive: function () 
6945     {
6946         return this.active;
6947     },
6948     
6949     setBadge : function(str)
6950     {
6951         if(!this.badgeEl){
6952             return;
6953         }
6954         
6955         this.badgeEl.dom.innerHTML = str;
6956     }
6957     
6958    
6959      
6960  
6961 });
6962  
6963
6964  /*
6965  * - LGPL
6966  *
6967  * nav progress bar
6968  * 
6969  */
6970
6971 /**
6972  * @class Roo.bootstrap.nav.ProgressBar
6973  * @extends Roo.bootstrap.Component
6974  * @children Roo.bootstrap.nav.ProgressBarItem
6975  * Bootstrap NavProgressBar class
6976  * 
6977  * @constructor
6978  * Create a new nav progress bar - a bar indicating step along a process
6979  * @param {Object} config The config object
6980  */
6981
6982 Roo.bootstrap.nav.ProgressBar = function(config){
6983     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6984
6985     this.bullets = this.bullets || [];
6986    
6987 //    Roo.bootstrap.nav.ProgressBar.register(this);
6988      this.addEvents({
6989         /**
6990              * @event changed
6991              * Fires when the active item changes
6992              * @param {Roo.bootstrap.nav.ProgressBar} this
6993              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6994              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6995          */
6996         'changed': true
6997      });
6998     
6999 };
7000
7001 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7002     /**
7003      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7004      * Bullets for the Nav Progress bar for the toolbar
7005      */
7006     bullets : [],
7007     barItems : [],
7008     
7009     getAutoCreate : function()
7010     {
7011         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7012         
7013         cfg = {
7014             tag : 'div',
7015             cls : 'roo-navigation-bar-group',
7016             cn : [
7017                 {
7018                     tag : 'div',
7019                     cls : 'roo-navigation-top-bar'
7020                 },
7021                 {
7022                     tag : 'div',
7023                     cls : 'roo-navigation-bullets-bar',
7024                     cn : [
7025                         {
7026                             tag : 'ul',
7027                             cls : 'roo-navigation-bar'
7028                         }
7029                     ]
7030                 },
7031                 
7032                 {
7033                     tag : 'div',
7034                     cls : 'roo-navigation-bottom-bar'
7035                 }
7036             ]
7037             
7038         };
7039         
7040         return cfg;
7041         
7042     },
7043     
7044     initEvents: function() 
7045     {
7046         
7047     },
7048     
7049     onRender : function(ct, position) 
7050     {
7051         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7052         
7053         if(this.bullets.length){
7054             Roo.each(this.bullets, function(b){
7055                this.addItem(b);
7056             }, this);
7057         }
7058         
7059         this.format();
7060         
7061     },
7062     
7063     addItem : function(cfg)
7064     {
7065         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7066         
7067         item.parentId = this.id;
7068         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7069         
7070         if(cfg.html){
7071             var top = new Roo.bootstrap.Element({
7072                 tag : 'div',
7073                 cls : 'roo-navigation-bar-text'
7074             });
7075             
7076             var bottom = new Roo.bootstrap.Element({
7077                 tag : 'div',
7078                 cls : 'roo-navigation-bar-text'
7079             });
7080             
7081             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7082             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7083             
7084             var topText = new Roo.bootstrap.Element({
7085                 tag : 'span',
7086                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7087             });
7088             
7089             var bottomText = new Roo.bootstrap.Element({
7090                 tag : 'span',
7091                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7092             });
7093             
7094             topText.onRender(top.el, null);
7095             bottomText.onRender(bottom.el, null);
7096             
7097             item.topEl = top;
7098             item.bottomEl = bottom;
7099         }
7100         
7101         this.barItems.push(item);
7102         
7103         return item;
7104     },
7105     
7106     getActive : function()
7107     {
7108         var active = false;
7109         
7110         Roo.each(this.barItems, function(v){
7111             
7112             if (!v.isActive()) {
7113                 return;
7114             }
7115             
7116             active = v;
7117             return false;
7118             
7119         });
7120         
7121         return active;
7122     },
7123     
7124     setActiveItem : function(item)
7125     {
7126         var prev = false;
7127         
7128         Roo.each(this.barItems, function(v){
7129             if (v.rid == item.rid) {
7130                 return ;
7131             }
7132             
7133             if (v.isActive()) {
7134                 v.setActive(false);
7135                 prev = v;
7136             }
7137         });
7138
7139         item.setActive(true);
7140         
7141         this.fireEvent('changed', this, item, prev);
7142     },
7143     
7144     getBarItem: function(rid)
7145     {
7146         var ret = false;
7147         
7148         Roo.each(this.barItems, function(e) {
7149             if (e.rid != rid) {
7150                 return;
7151             }
7152             
7153             ret =  e;
7154             return false;
7155         });
7156         
7157         return ret;
7158     },
7159     
7160     indexOfItem : function(item)
7161     {
7162         var index = false;
7163         
7164         Roo.each(this.barItems, function(v, i){
7165             
7166             if (v.rid != item.rid) {
7167                 return;
7168             }
7169             
7170             index = i;
7171             return false
7172         });
7173         
7174         return index;
7175     },
7176     
7177     setActiveNext : function()
7178     {
7179         var i = this.indexOfItem(this.getActive());
7180         
7181         if (i > this.barItems.length) {
7182             return;
7183         }
7184         
7185         this.setActiveItem(this.barItems[i+1]);
7186     },
7187     
7188     setActivePrev : function()
7189     {
7190         var i = this.indexOfItem(this.getActive());
7191         
7192         if (i  < 1) {
7193             return;
7194         }
7195         
7196         this.setActiveItem(this.barItems[i-1]);
7197     },
7198     
7199     format : function()
7200     {
7201         if(!this.barItems.length){
7202             return;
7203         }
7204      
7205         var width = 100 / this.barItems.length;
7206         
7207         Roo.each(this.barItems, function(i){
7208             i.el.setStyle('width', width + '%');
7209             i.topEl.el.setStyle('width', width + '%');
7210             i.bottomEl.el.setStyle('width', width + '%');
7211         }, this);
7212         
7213     }
7214     
7215 });
7216 /*
7217  * - LGPL
7218  *
7219  * Nav Progress Item
7220  * 
7221  */
7222
7223 /**
7224  * @class Roo.bootstrap.nav.ProgressBarItem
7225  * @extends Roo.bootstrap.Component
7226  * Bootstrap NavProgressBarItem class
7227  * @cfg {String} rid the reference id
7228  * @cfg {Boolean} active (true|false) Is item active default false
7229  * @cfg {Boolean} disabled (true|false) Is item active default false
7230  * @cfg {String} html
7231  * @cfg {String} position (top|bottom) text position default bottom
7232  * @cfg {String} icon show icon instead of number
7233  * 
7234  * @constructor
7235  * Create a new NavProgressBarItem
7236  * @param {Object} config The config object
7237  */
7238 Roo.bootstrap.nav.ProgressBarItem = function(config){
7239     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7240     this.addEvents({
7241         // raw events
7242         /**
7243          * @event click
7244          * The raw click event for the entire grid.
7245          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7246          * @param {Roo.EventObject} e
7247          */
7248         "click" : true
7249     });
7250    
7251 };
7252
7253 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7254     
7255     rid : '',
7256     active : false,
7257     disabled : false,
7258     html : '',
7259     position : 'bottom',
7260     icon : false,
7261     
7262     getAutoCreate : function()
7263     {
7264         var iconCls = 'roo-navigation-bar-item-icon';
7265         
7266         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7267         
7268         var cfg = {
7269             tag: 'li',
7270             cls: 'roo-navigation-bar-item',
7271             cn : [
7272                 {
7273                     tag : 'i',
7274                     cls : iconCls
7275                 }
7276             ]
7277         };
7278         
7279         if(this.active){
7280             cfg.cls += ' active';
7281         }
7282         if(this.disabled){
7283             cfg.cls += ' disabled';
7284         }
7285         
7286         return cfg;
7287     },
7288     
7289     disable : function()
7290     {
7291         this.setDisabled(true);
7292     },
7293     
7294     enable : function()
7295     {
7296         this.setDisabled(false);
7297     },
7298     
7299     initEvents: function() 
7300     {
7301         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7302         
7303         this.iconEl.on('click', this.onClick, this);
7304     },
7305     
7306     onClick : function(e)
7307     {
7308         e.preventDefault();
7309         
7310         if(this.disabled){
7311             return;
7312         }
7313         
7314         if(this.fireEvent('click', this, e) === false){
7315             return;
7316         };
7317         
7318         this.parent().setActiveItem(this);
7319     },
7320     
7321     isActive: function () 
7322     {
7323         return this.active;
7324     },
7325     
7326     setActive : function(state)
7327     {
7328         if(this.active == state){
7329             return;
7330         }
7331         
7332         this.active = state;
7333         
7334         if (state) {
7335             this.el.addClass('active');
7336             return;
7337         }
7338         
7339         this.el.removeClass('active');
7340         
7341         return;
7342     },
7343     
7344     setDisabled : function(state)
7345     {
7346         if(this.disabled == state){
7347             return;
7348         }
7349         
7350         this.disabled = state;
7351         
7352         if (state) {
7353             this.el.addClass('disabled');
7354             return;
7355         }
7356         
7357         this.el.removeClass('disabled');
7358     },
7359     
7360     tooltipEl : function()
7361     {
7362         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7363     }
7364 });
7365  
7366
7367  /*
7368  * - LGPL
7369  *
7370  *  Breadcrumb Nav
7371  * 
7372  */
7373 Roo.namespace('Roo.bootstrap.breadcrumb');
7374
7375
7376 /**
7377  * @class Roo.bootstrap.breadcrumb.Nav
7378  * @extends Roo.bootstrap.Component
7379  * Bootstrap Breadcrumb Nav Class
7380  *  
7381  * @children Roo.bootstrap.breadcrumb.Item
7382  * 
7383  * @constructor
7384  * Create a new breadcrumb.Nav
7385  * @param {Object} config The config object
7386  */
7387
7388
7389 Roo.bootstrap.breadcrumb.Nav = function(config){
7390     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7391     
7392     
7393 };
7394
7395 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7396     
7397     getAutoCreate : function()
7398     {
7399
7400         var cfg = {
7401             tag: 'nav',
7402             cn : [
7403                 {
7404                     tag : 'ol',
7405                     cls : 'breadcrumb'
7406                 }
7407             ]
7408             
7409         };
7410           
7411         return cfg;
7412     },
7413     
7414     initEvents: function()
7415     {
7416         this.olEl = this.el.select('ol',true).first();    
7417     },
7418     getChildContainer : function()
7419     {
7420         return this.olEl;  
7421     }
7422     
7423 });
7424
7425  /*
7426  * - LGPL
7427  *
7428  *  Breadcrumb Item
7429  * 
7430  */
7431
7432
7433 /**
7434  * @class Roo.bootstrap.breadcrumb.Nav
7435  * @extends Roo.bootstrap.Component
7436  * @children Roo.bootstrap.Component
7437  * @parent Roo.bootstrap.breadcrumb.Nav
7438  * Bootstrap Breadcrumb Nav Class
7439  *  
7440  * 
7441  * @cfg {String} html the content of the link.
7442  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7443  * @cfg {Boolean} active is it active
7444
7445  * 
7446  * @constructor
7447  * Create a new breadcrumb.Nav
7448  * @param {Object} config The config object
7449  */
7450
7451 Roo.bootstrap.breadcrumb.Item = function(config){
7452     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7453     this.addEvents({
7454         // img events
7455         /**
7456          * @event click
7457          * The img click event for the img.
7458          * @param {Roo.EventObject} e
7459          */
7460         "click" : true
7461     });
7462     
7463 };
7464
7465 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7466     
7467     href: false,
7468     html : '',
7469     
7470     getAutoCreate : function()
7471     {
7472
7473         var cfg = {
7474             tag: 'li',
7475             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7476         };
7477         if (this.href !== false) {
7478             cfg.cn = [{
7479                 tag : 'a',
7480                 href : this.href,
7481                 html : this.html
7482             }];
7483         } else {
7484             cfg.html = this.html;
7485         }
7486         
7487         return cfg;
7488     },
7489     
7490     initEvents: function()
7491     {
7492         if (this.href) {
7493             this.el.select('a', true).first().on('click',this.onClick, this)
7494         }
7495         
7496     },
7497     onClick : function(e)
7498     {
7499         e.preventDefault();
7500         this.fireEvent('click',this,  e);
7501     }
7502     
7503 });
7504
7505  /*
7506  * - LGPL
7507  *
7508  * row
7509  * 
7510  */
7511
7512 /**
7513  * @class Roo.bootstrap.Row
7514  * @extends Roo.bootstrap.Component
7515  * @children Roo.bootstrap.Component
7516  * Bootstrap Row class (contains columns...)
7517  * 
7518  * @constructor
7519  * Create a new Row
7520  * @param {Object} config The config object
7521  */
7522
7523 Roo.bootstrap.Row = function(config){
7524     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7525 };
7526
7527 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7528     
7529     getAutoCreate : function(){
7530        return {
7531             cls: 'row clearfix'
7532        };
7533     }
7534     
7535     
7536 });
7537
7538  
7539
7540  /*
7541  * - LGPL
7542  *
7543  * pagination
7544  * 
7545  */
7546
7547 /**
7548  * @class Roo.bootstrap.Pagination
7549  * @extends Roo.bootstrap.Component
7550  * @children Roo.bootstrap.Pagination
7551  * Bootstrap Pagination class
7552  * 
7553  * @cfg {String} size (xs|sm|md|lg|xl)
7554  * @cfg {Boolean} inverse 
7555  * 
7556  * @constructor
7557  * Create a new Pagination
7558  * @param {Object} config The config object
7559  */
7560
7561 Roo.bootstrap.Pagination = function(config){
7562     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7563 };
7564
7565 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7566     
7567     cls: false,
7568     size: false,
7569     inverse: false,
7570     
7571     getAutoCreate : function(){
7572         var cfg = {
7573             tag: 'ul',
7574                 cls: 'pagination'
7575         };
7576         if (this.inverse) {
7577             cfg.cls += ' inverse';
7578         }
7579         if (this.html) {
7580             cfg.html=this.html;
7581         }
7582         if (this.cls) {
7583             cfg.cls += " " + this.cls;
7584         }
7585         return cfg;
7586     }
7587    
7588 });
7589
7590  
7591
7592  /*
7593  * - LGPL
7594  *
7595  * Pagination item
7596  * 
7597  */
7598
7599
7600 /**
7601  * @class Roo.bootstrap.PaginationItem
7602  * @extends Roo.bootstrap.Component
7603  * Bootstrap PaginationItem class
7604  * @cfg {String} html text
7605  * @cfg {String} href the link
7606  * @cfg {Boolean} preventDefault (true | false) default true
7607  * @cfg {Boolean} active (true | false) default false
7608  * @cfg {Boolean} disabled default false
7609  * 
7610  * 
7611  * @constructor
7612  * Create a new PaginationItem
7613  * @param {Object} config The config object
7614  */
7615
7616
7617 Roo.bootstrap.PaginationItem = function(config){
7618     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7619     this.addEvents({
7620         // raw events
7621         /**
7622          * @event click
7623          * The raw click event for the entire grid.
7624          * @param {Roo.EventObject} e
7625          */
7626         "click" : true
7627     });
7628 };
7629
7630 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7631     
7632     href : false,
7633     html : false,
7634     preventDefault: true,
7635     active : false,
7636     cls : false,
7637     disabled: false,
7638     
7639     getAutoCreate : function(){
7640         var cfg= {
7641             tag: 'li',
7642             cn: [
7643                 {
7644                     tag : 'a',
7645                     href : this.href ? this.href : '#',
7646                     html : this.html ? this.html : ''
7647                 }
7648             ]
7649         };
7650         
7651         if(this.cls){
7652             cfg.cls = this.cls;
7653         }
7654         
7655         if(this.disabled){
7656             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7657         }
7658         
7659         if(this.active){
7660             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7661         }
7662         
7663         return cfg;
7664     },
7665     
7666     initEvents: function() {
7667         
7668         this.el.on('click', this.onClick, this);
7669         
7670     },
7671     onClick : function(e)
7672     {
7673         Roo.log('PaginationItem on click ');
7674         if(this.preventDefault){
7675             e.preventDefault();
7676         }
7677         
7678         if(this.disabled){
7679             return;
7680         }
7681         
7682         this.fireEvent('click', this, e);
7683     }
7684    
7685 });
7686
7687  
7688
7689  /*
7690  * - LGPL
7691  *
7692  * slider
7693  * 
7694  */
7695
7696
7697 /**
7698  * @class Roo.bootstrap.Slider
7699  * @extends Roo.bootstrap.Component
7700  * Bootstrap Slider class
7701  *    
7702  * @constructor
7703  * Create a new Slider
7704  * @param {Object} config The config object
7705  */
7706
7707 Roo.bootstrap.Slider = function(config){
7708     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7709 };
7710
7711 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7712     
7713     getAutoCreate : function(){
7714         
7715         var cfg = {
7716             tag: 'div',
7717             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7718             cn: [
7719                 {
7720                     tag: 'a',
7721                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7722                 }
7723             ]
7724         };
7725         
7726         return cfg;
7727     }
7728    
7729 });
7730
7731  /*
7732  * Based on:
7733  * Ext JS Library 1.1.1
7734  * Copyright(c) 2006-2007, Ext JS, LLC.
7735  *
7736  * Originally Released Under LGPL - original licence link has changed is not relivant.
7737  *
7738  * Fork - LGPL
7739  * <script type="text/javascript">
7740  */
7741  /**
7742  * @extends Roo.dd.DDProxy
7743  * @class Roo.grid.SplitDragZone
7744  * Support for Column Header resizing
7745  * @constructor
7746  * @param {Object} config
7747  */
7748 // private
7749 // This is a support class used internally by the Grid components
7750 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7751     this.grid = grid;
7752     this.view = grid.getView();
7753     this.proxy = this.view.resizeProxy;
7754     Roo.grid.SplitDragZone.superclass.constructor.call(
7755         this,
7756         hd, // ID
7757         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7758         {  // CONFIG
7759             dragElId : Roo.id(this.proxy.dom),
7760             resizeFrame:false
7761         }
7762     );
7763     
7764     this.setHandleElId(Roo.id(hd));
7765     if (hd2 !== false) {
7766         this.setOuterHandleElId(Roo.id(hd2));
7767     }
7768     
7769     this.scroll = false;
7770 };
7771 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7772     fly: Roo.Element.fly,
7773
7774     b4StartDrag : function(x, y){
7775         this.view.headersDisabled = true;
7776         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7777                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7778         );
7779         this.proxy.setHeight(h);
7780         
7781         // for old system colWidth really stored the actual width?
7782         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7783         // which in reality did not work.. - it worked only for fixed sizes
7784         // for resizable we need to use actual sizes.
7785         var w = this.cm.getColumnWidth(this.cellIndex);
7786         if (!this.view.mainWrap) {
7787             // bootstrap.
7788             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7789         }
7790         
7791         
7792         
7793         // this was w-this.grid.minColumnWidth;
7794         // doesnt really make sense? - w = thie curren width or the rendered one?
7795         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7796         this.resetConstraints();
7797         this.setXConstraint(minw, 1000);
7798         this.setYConstraint(0, 0);
7799         this.minX = x - minw;
7800         this.maxX = x + 1000;
7801         this.startPos = x;
7802         if (!this.view.mainWrap) { // this is Bootstrap code..
7803             this.getDragEl().style.display='block';
7804         }
7805         
7806         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7807     },
7808
7809
7810     handleMouseDown : function(e){
7811         ev = Roo.EventObject.setEvent(e);
7812         var t = this.fly(ev.getTarget());
7813         if(t.hasClass("x-grid-split")){
7814             this.cellIndex = this.view.getCellIndex(t.dom);
7815             this.split = t.dom;
7816             this.cm = this.grid.colModel;
7817             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7818                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7819             }
7820         }
7821     },
7822
7823     endDrag : function(e){
7824         this.view.headersDisabled = false;
7825         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7826         var diff = endX - this.startPos;
7827         // 
7828         var w = this.cm.getColumnWidth(this.cellIndex);
7829         if (!this.view.mainWrap) {
7830             w = 0;
7831         }
7832         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7833     },
7834
7835     autoOffset : function(){
7836         this.setDelta(0,0);
7837     }
7838 });/*
7839  * Based on:
7840  * Ext JS Library 1.1.1
7841  * Copyright(c) 2006-2007, Ext JS, LLC.
7842  *
7843  * Originally Released Under LGPL - original licence link has changed is not relivant.
7844  *
7845  * Fork - LGPL
7846  * <script type="text/javascript">
7847  */
7848
7849 /**
7850  * @class Roo.grid.AbstractSelectionModel
7851  * @extends Roo.util.Observable
7852  * @abstract
7853  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7854  * implemented by descendant classes.  This class should not be directly instantiated.
7855  * @constructor
7856  */
7857 Roo.grid.AbstractSelectionModel = function(){
7858     this.locked = false;
7859     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7860 };
7861
7862 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7863     /** @ignore Called by the grid automatically. Do not call directly. */
7864     init : function(grid){
7865         this.grid = grid;
7866         this.initEvents();
7867     },
7868
7869     /**
7870      * Locks the selections.
7871      */
7872     lock : function(){
7873         this.locked = true;
7874     },
7875
7876     /**
7877      * Unlocks the selections.
7878      */
7879     unlock : function(){
7880         this.locked = false;
7881     },
7882
7883     /**
7884      * Returns true if the selections are locked.
7885      * @return {Boolean}
7886      */
7887     isLocked : function(){
7888         return this.locked;
7889     }
7890 });/*
7891  * Based on:
7892  * Ext JS Library 1.1.1
7893  * Copyright(c) 2006-2007, Ext JS, LLC.
7894  *
7895  * Originally Released Under LGPL - original licence link has changed is not relivant.
7896  *
7897  * Fork - LGPL
7898  * <script type="text/javascript">
7899  */
7900 /**
7901  * @extends Roo.grid.AbstractSelectionModel
7902  * @class Roo.grid.RowSelectionModel
7903  * The default SelectionModel used by {@link Roo.grid.Grid}.
7904  * It supports multiple selections and keyboard selection/navigation. 
7905  * @constructor
7906  * @param {Object} config
7907  */
7908 Roo.grid.RowSelectionModel = function(config){
7909     Roo.apply(this, config);
7910     this.selections = new Roo.util.MixedCollection(false, function(o){
7911         return o.id;
7912     });
7913
7914     this.last = false;
7915     this.lastActive = false;
7916
7917     this.addEvents({
7918         /**
7919         * @event selectionchange
7920         * Fires when the selection changes
7921         * @param {SelectionModel} this
7922         */
7923        "selectionchange" : true,
7924        /**
7925         * @event afterselectionchange
7926         * Fires after the selection changes (eg. by key press or clicking)
7927         * @param {SelectionModel} this
7928         */
7929        "afterselectionchange" : true,
7930        /**
7931         * @event beforerowselect
7932         * Fires when a row is selected being selected, return false to cancel.
7933         * @param {SelectionModel} this
7934         * @param {Number} rowIndex The selected index
7935         * @param {Boolean} keepExisting False if other selections will be cleared
7936         */
7937        "beforerowselect" : true,
7938        /**
7939         * @event rowselect
7940         * Fires when a row is selected.
7941         * @param {SelectionModel} this
7942         * @param {Number} rowIndex The selected index
7943         * @param {Roo.data.Record} r The record
7944         */
7945        "rowselect" : true,
7946        /**
7947         * @event rowdeselect
7948         * Fires when a row is deselected.
7949         * @param {SelectionModel} this
7950         * @param {Number} rowIndex The selected index
7951         */
7952         "rowdeselect" : true
7953     });
7954     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7955     this.locked = false;
7956 };
7957
7958 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7959     /**
7960      * @cfg {Boolean} singleSelect
7961      * True to allow selection of only one row at a time (defaults to false)
7962      */
7963     singleSelect : false,
7964
7965     // private
7966     initEvents : function(){
7967
7968         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7969             this.grid.on("mousedown", this.handleMouseDown, this);
7970         }else{ // allow click to work like normal
7971             this.grid.on("rowclick", this.handleDragableRowClick, this);
7972         }
7973         // bootstrap does not have a view..
7974         var view = this.grid.view ? this.grid.view : this.grid;
7975         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7976             "up" : function(e){
7977                 if(!e.shiftKey){
7978                     this.selectPrevious(e.shiftKey);
7979                 }else if(this.last !== false && this.lastActive !== false){
7980                     var last = this.last;
7981                     this.selectRange(this.last,  this.lastActive-1);
7982                     view.focusRow(this.lastActive);
7983                     if(last !== false){
7984                         this.last = last;
7985                     }
7986                 }else{
7987                     this.selectFirstRow();
7988                 }
7989                 this.fireEvent("afterselectionchange", this);
7990             },
7991             "down" : function(e){
7992                 if(!e.shiftKey){
7993                     this.selectNext(e.shiftKey);
7994                 }else if(this.last !== false && this.lastActive !== false){
7995                     var last = this.last;
7996                     this.selectRange(this.last,  this.lastActive+1);
7997                     view.focusRow(this.lastActive);
7998                     if(last !== false){
7999                         this.last = last;
8000                     }
8001                 }else{
8002                     this.selectFirstRow();
8003                 }
8004                 this.fireEvent("afterselectionchange", this);
8005             },
8006             scope: this
8007         });
8008
8009          
8010         view.on("refresh", this.onRefresh, this);
8011         view.on("rowupdated", this.onRowUpdated, this);
8012         view.on("rowremoved", this.onRemove, this);
8013     },
8014
8015     // private
8016     onRefresh : function(){
8017         var ds = this.grid.ds, i, v = this.grid.view;
8018         var s = this.selections;
8019         s.each(function(r){
8020             if((i = ds.indexOfId(r.id)) != -1){
8021                 v.onRowSelect(i);
8022                 s.add(ds.getAt(i)); // updating the selection relate data
8023             }else{
8024                 s.remove(r);
8025             }
8026         });
8027     },
8028
8029     // private
8030     onRemove : function(v, index, r){
8031         this.selections.remove(r);
8032     },
8033
8034     // private
8035     onRowUpdated : function(v, index, r){
8036         if(this.isSelected(r)){
8037             v.onRowSelect(index);
8038         }
8039     },
8040
8041     /**
8042      * Select records.
8043      * @param {Array} records The records to select
8044      * @param {Boolean} keepExisting (optional) True to keep existing selections
8045      */
8046     selectRecords : function(records, keepExisting){
8047         if(!keepExisting){
8048             this.clearSelections();
8049         }
8050         var ds = this.grid.ds;
8051         for(var i = 0, len = records.length; i < len; i++){
8052             this.selectRow(ds.indexOf(records[i]), true);
8053         }
8054     },
8055
8056     /**
8057      * Gets the number of selected rows.
8058      * @return {Number}
8059      */
8060     getCount : function(){
8061         return this.selections.length;
8062     },
8063
8064     /**
8065      * Selects the first row in the grid.
8066      */
8067     selectFirstRow : function(){
8068         this.selectRow(0);
8069     },
8070
8071     /**
8072      * Select the last row.
8073      * @param {Boolean} keepExisting (optional) True to keep existing selections
8074      */
8075     selectLastRow : function(keepExisting){
8076         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8077     },
8078
8079     /**
8080      * Selects the row immediately following the last selected row.
8081      * @param {Boolean} keepExisting (optional) True to keep existing selections
8082      */
8083     selectNext : function(keepExisting){
8084         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8085             this.selectRow(this.last+1, keepExisting);
8086             var view = this.grid.view ? this.grid.view : this.grid;
8087             view.focusRow(this.last);
8088         }
8089     },
8090
8091     /**
8092      * Selects the row that precedes the last selected row.
8093      * @param {Boolean} keepExisting (optional) True to keep existing selections
8094      */
8095     selectPrevious : function(keepExisting){
8096         if(this.last){
8097             this.selectRow(this.last-1, keepExisting);
8098             var view = this.grid.view ? this.grid.view : this.grid;
8099             view.focusRow(this.last);
8100         }
8101     },
8102
8103     /**
8104      * Returns the selected records
8105      * @return {Array} Array of selected records
8106      */
8107     getSelections : function(){
8108         return [].concat(this.selections.items);
8109     },
8110
8111     /**
8112      * Returns the first selected record.
8113      * @return {Record}
8114      */
8115     getSelected : function(){
8116         return this.selections.itemAt(0);
8117     },
8118
8119
8120     /**
8121      * Clears all selections.
8122      */
8123     clearSelections : function(fast){
8124         if(this.locked) {
8125             return;
8126         }
8127         if(fast !== true){
8128             var ds = this.grid.ds;
8129             var s = this.selections;
8130             s.each(function(r){
8131                 this.deselectRow(ds.indexOfId(r.id));
8132             }, this);
8133             s.clear();
8134         }else{
8135             this.selections.clear();
8136         }
8137         this.last = false;
8138     },
8139
8140
8141     /**
8142      * Selects all rows.
8143      */
8144     selectAll : function(){
8145         if(this.locked) {
8146             return;
8147         }
8148         this.selections.clear();
8149         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8150             this.selectRow(i, true);
8151         }
8152     },
8153
8154     /**
8155      * Returns True if there is a selection.
8156      * @return {Boolean}
8157      */
8158     hasSelection : function(){
8159         return this.selections.length > 0;
8160     },
8161
8162     /**
8163      * Returns True if the specified row is selected.
8164      * @param {Number/Record} record The record or index of the record to check
8165      * @return {Boolean}
8166      */
8167     isSelected : function(index){
8168         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8169         return (r && this.selections.key(r.id) ? true : false);
8170     },
8171
8172     /**
8173      * Returns True if the specified record id is selected.
8174      * @param {String} id The id of record to check
8175      * @return {Boolean}
8176      */
8177     isIdSelected : function(id){
8178         return (this.selections.key(id) ? true : false);
8179     },
8180
8181     // private
8182     handleMouseDown : function(e, t)
8183     {
8184         var view = this.grid.view ? this.grid.view : this.grid;
8185         var rowIndex;
8186         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8187             return;
8188         };
8189         if(e.shiftKey && this.last !== false){
8190             var last = this.last;
8191             this.selectRange(last, rowIndex, e.ctrlKey);
8192             this.last = last; // reset the last
8193             view.focusRow(rowIndex);
8194         }else{
8195             var isSelected = this.isSelected(rowIndex);
8196             if(e.button !== 0 && isSelected){
8197                 view.focusRow(rowIndex);
8198             }else if(e.ctrlKey && isSelected){
8199                 this.deselectRow(rowIndex);
8200             }else if(!isSelected){
8201                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8202                 view.focusRow(rowIndex);
8203             }
8204         }
8205         this.fireEvent("afterselectionchange", this);
8206     },
8207     // private
8208     handleDragableRowClick :  function(grid, rowIndex, e) 
8209     {
8210         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8211             this.selectRow(rowIndex, false);
8212             var view = this.grid.view ? this.grid.view : this.grid;
8213             view.focusRow(rowIndex);
8214              this.fireEvent("afterselectionchange", this);
8215         }
8216     },
8217     
8218     /**
8219      * Selects multiple rows.
8220      * @param {Array} rows Array of the indexes of the row to select
8221      * @param {Boolean} keepExisting (optional) True to keep existing selections
8222      */
8223     selectRows : function(rows, keepExisting){
8224         if(!keepExisting){
8225             this.clearSelections();
8226         }
8227         for(var i = 0, len = rows.length; i < len; i++){
8228             this.selectRow(rows[i], true);
8229         }
8230     },
8231
8232     /**
8233      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8234      * @param {Number} startRow The index of the first row in the range
8235      * @param {Number} endRow The index of the last row in the range
8236      * @param {Boolean} keepExisting (optional) True to retain existing selections
8237      */
8238     selectRange : function(startRow, endRow, keepExisting){
8239         if(this.locked) {
8240             return;
8241         }
8242         if(!keepExisting){
8243             this.clearSelections();
8244         }
8245         if(startRow <= endRow){
8246             for(var i = startRow; i <= endRow; i++){
8247                 this.selectRow(i, true);
8248             }
8249         }else{
8250             for(var i = startRow; i >= endRow; i--){
8251                 this.selectRow(i, true);
8252             }
8253         }
8254     },
8255
8256     /**
8257      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8258      * @param {Number} startRow The index of the first row in the range
8259      * @param {Number} endRow The index of the last row in the range
8260      */
8261     deselectRange : function(startRow, endRow, preventViewNotify){
8262         if(this.locked) {
8263             return;
8264         }
8265         for(var i = startRow; i <= endRow; i++){
8266             this.deselectRow(i, preventViewNotify);
8267         }
8268     },
8269
8270     /**
8271      * Selects a row.
8272      * @param {Number} row The index of the row to select
8273      * @param {Boolean} keepExisting (optional) True to keep existing selections
8274      */
8275     selectRow : function(index, keepExisting, preventViewNotify){
8276         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8277             return;
8278         }
8279         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8280             if(!keepExisting || this.singleSelect){
8281                 this.clearSelections();
8282             }
8283             var r = this.grid.ds.getAt(index);
8284             this.selections.add(r);
8285             this.last = this.lastActive = index;
8286             if(!preventViewNotify){
8287                 var view = this.grid.view ? this.grid.view : this.grid;
8288                 view.onRowSelect(index);
8289             }
8290             this.fireEvent("rowselect", this, index, r);
8291             this.fireEvent("selectionchange", this);
8292         }
8293     },
8294
8295     /**
8296      * Deselects a row.
8297      * @param {Number} row The index of the row to deselect
8298      */
8299     deselectRow : function(index, preventViewNotify){
8300         if(this.locked) {
8301             return;
8302         }
8303         if(this.last == index){
8304             this.last = false;
8305         }
8306         if(this.lastActive == index){
8307             this.lastActive = false;
8308         }
8309         var r = this.grid.ds.getAt(index);
8310         this.selections.remove(r);
8311         if(!preventViewNotify){
8312             var view = this.grid.view ? this.grid.view : this.grid;
8313             view.onRowDeselect(index);
8314         }
8315         this.fireEvent("rowdeselect", this, index);
8316         this.fireEvent("selectionchange", this);
8317     },
8318
8319     // private
8320     restoreLast : function(){
8321         if(this._last){
8322             this.last = this._last;
8323         }
8324     },
8325
8326     // private
8327     acceptsNav : function(row, col, cm){
8328         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8329     },
8330
8331     // private
8332     onEditorKey : function(field, e){
8333         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8334         if(k == e.TAB){
8335             e.stopEvent();
8336             ed.completeEdit();
8337             if(e.shiftKey){
8338                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8339             }else{
8340                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8341             }
8342         }else if(k == e.ENTER && !e.ctrlKey){
8343             e.stopEvent();
8344             ed.completeEdit();
8345             if(e.shiftKey){
8346                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8347             }else{
8348                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8349             }
8350         }else if(k == e.ESC){
8351             ed.cancelEdit();
8352         }
8353         if(newCell){
8354             g.startEditing(newCell[0], newCell[1]);
8355         }
8356     }
8357 });/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367  
8368
8369 /**
8370  * @class Roo.grid.ColumnModel
8371  * @extends Roo.util.Observable
8372  * This is the default implementation of a ColumnModel used by the Grid. It defines
8373  * the columns in the grid.
8374  * <br>Usage:<br>
8375  <pre><code>
8376  var colModel = new Roo.grid.ColumnModel([
8377         {header: "Ticker", width: 60, sortable: true, locked: true},
8378         {header: "Company Name", width: 150, sortable: true},
8379         {header: "Market Cap.", width: 100, sortable: true},
8380         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8381         {header: "Employees", width: 100, sortable: true, resizable: false}
8382  ]);
8383  </code></pre>
8384  * <p>
8385  
8386  * The config options listed for this class are options which may appear in each
8387  * individual column definition.
8388  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8389  * @constructor
8390  * @param {Object} config An Array of column config objects. See this class's
8391  * config objects for details.
8392 */
8393 Roo.grid.ColumnModel = function(config){
8394         /**
8395      * The config passed into the constructor
8396      */
8397     this.config = []; //config;
8398     this.lookup = {};
8399
8400     // if no id, create one
8401     // if the column does not have a dataIndex mapping,
8402     // map it to the order it is in the config
8403     for(var i = 0, len = config.length; i < len; i++){
8404         this.addColumn(config[i]);
8405         
8406     }
8407
8408     /**
8409      * The width of columns which have no width specified (defaults to 100)
8410      * @type Number
8411      */
8412     this.defaultWidth = 100;
8413
8414     /**
8415      * Default sortable of columns which have no sortable specified (defaults to false)
8416      * @type Boolean
8417      */
8418     this.defaultSortable = false;
8419
8420     this.addEvents({
8421         /**
8422              * @event widthchange
8423              * Fires when the width of a column changes.
8424              * @param {ColumnModel} this
8425              * @param {Number} columnIndex The column index
8426              * @param {Number} newWidth The new width
8427              */
8428             "widthchange": true,
8429         /**
8430              * @event headerchange
8431              * Fires when the text of a header changes.
8432              * @param {ColumnModel} this
8433              * @param {Number} columnIndex The column index
8434              * @param {Number} newText The new header text
8435              */
8436             "headerchange": true,
8437         /**
8438              * @event hiddenchange
8439              * Fires when a column is hidden or "unhidden".
8440              * @param {ColumnModel} this
8441              * @param {Number} columnIndex The column index
8442              * @param {Boolean} hidden true if hidden, false otherwise
8443              */
8444             "hiddenchange": true,
8445             /**
8446          * @event columnmoved
8447          * Fires when a column is moved.
8448          * @param {ColumnModel} this
8449          * @param {Number} oldIndex
8450          * @param {Number} newIndex
8451          */
8452         "columnmoved" : true,
8453         /**
8454          * @event columlockchange
8455          * Fires when a column's locked state is changed
8456          * @param {ColumnModel} this
8457          * @param {Number} colIndex
8458          * @param {Boolean} locked true if locked
8459          */
8460         "columnlockchange" : true
8461     });
8462     Roo.grid.ColumnModel.superclass.constructor.call(this);
8463 };
8464 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8465     /**
8466      * @cfg {String} header [required] The header text to display in the Grid view.
8467      */
8468         /**
8469      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8470      */
8471         /**
8472      * @cfg {String} smHeader Header at Bootsrap Small width
8473      */
8474         /**
8475      * @cfg {String} mdHeader Header at Bootsrap Medium width
8476      */
8477         /**
8478      * @cfg {String} lgHeader Header at Bootsrap Large width
8479      */
8480         /**
8481      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8482      */
8483     /**
8484      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8485      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8486      * specified, the column's index is used as an index into the Record's data Array.
8487      */
8488     /**
8489      * @cfg {Number} width  The initial width in pixels of the column. Using this
8490      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8491      */
8492     /**
8493      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8494      * Defaults to the value of the {@link #defaultSortable} property.
8495      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8496      */
8497     /**
8498      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8499      */
8500     /**
8501      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8502      */
8503     /**
8504      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8505      */
8506     /**
8507      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8508      */
8509     /**
8510      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8511      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8512      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8513      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8514      */
8515        /**
8516      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8517      */
8518     /**
8519      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8520      */
8521     /**
8522      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8523      */
8524     /**
8525      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
8526      */
8527     /**
8528      * @cfg {String} tooltip mouse over tooltip text
8529      */
8530     /**
8531      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8532      */
8533     /**
8534      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8535      */
8536     /**
8537      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8538      */
8539     /**
8540      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8541      */
8542         /**
8543      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8544      */
8545     /**
8546      * Returns the id of the column at the specified index.
8547      * @param {Number} index The column index
8548      * @return {String} the id
8549      */
8550     getColumnId : function(index){
8551         return this.config[index].id;
8552     },
8553
8554     /**
8555      * Returns the column for a specified id.
8556      * @param {String} id The column id
8557      * @return {Object} the column
8558      */
8559     getColumnById : function(id){
8560         return this.lookup[id];
8561     },
8562
8563     
8564     /**
8565      * Returns the column Object for a specified dataIndex.
8566      * @param {String} dataIndex The column dataIndex
8567      * @return {Object|Boolean} the column or false if not found
8568      */
8569     getColumnByDataIndex: function(dataIndex){
8570         var index = this.findColumnIndex(dataIndex);
8571         return index > -1 ? this.config[index] : false;
8572     },
8573     
8574     /**
8575      * Returns the index for a specified column id.
8576      * @param {String} id The column id
8577      * @return {Number} the index, or -1 if not found
8578      */
8579     getIndexById : function(id){
8580         for(var i = 0, len = this.config.length; i < len; i++){
8581             if(this.config[i].id == id){
8582                 return i;
8583             }
8584         }
8585         return -1;
8586     },
8587     
8588     /**
8589      * Returns the index for a specified column dataIndex.
8590      * @param {String} dataIndex The column dataIndex
8591      * @return {Number} the index, or -1 if not found
8592      */
8593     
8594     findColumnIndex : function(dataIndex){
8595         for(var i = 0, len = this.config.length; i < len; i++){
8596             if(this.config[i].dataIndex == dataIndex){
8597                 return i;
8598             }
8599         }
8600         return -1;
8601     },
8602     
8603     
8604     moveColumn : function(oldIndex, newIndex){
8605         var c = this.config[oldIndex];
8606         this.config.splice(oldIndex, 1);
8607         this.config.splice(newIndex, 0, c);
8608         this.dataMap = null;
8609         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8610     },
8611
8612     isLocked : function(colIndex){
8613         return this.config[colIndex].locked === true;
8614     },
8615
8616     setLocked : function(colIndex, value, suppressEvent){
8617         if(this.isLocked(colIndex) == value){
8618             return;
8619         }
8620         this.config[colIndex].locked = value;
8621         if(!suppressEvent){
8622             this.fireEvent("columnlockchange", this, colIndex, value);
8623         }
8624     },
8625
8626     getTotalLockedWidth : function(){
8627         var totalWidth = 0;
8628         for(var i = 0; i < this.config.length; i++){
8629             if(this.isLocked(i) && !this.isHidden(i)){
8630                 this.totalWidth += this.getColumnWidth(i);
8631             }
8632         }
8633         return totalWidth;
8634     },
8635
8636     getLockedCount : function(){
8637         for(var i = 0, len = this.config.length; i < len; i++){
8638             if(!this.isLocked(i)){
8639                 return i;
8640             }
8641         }
8642         
8643         return this.config.length;
8644     },
8645
8646     /**
8647      * Returns the number of columns.
8648      * @return {Number}
8649      */
8650     getColumnCount : function(visibleOnly){
8651         if(visibleOnly === true){
8652             var c = 0;
8653             for(var i = 0, len = this.config.length; i < len; i++){
8654                 if(!this.isHidden(i)){
8655                     c++;
8656                 }
8657             }
8658             return c;
8659         }
8660         return this.config.length;
8661     },
8662
8663     /**
8664      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8665      * @param {Function} fn
8666      * @param {Object} scope (optional)
8667      * @return {Array} result
8668      */
8669     getColumnsBy : function(fn, scope){
8670         var r = [];
8671         for(var i = 0, len = this.config.length; i < len; i++){
8672             var c = this.config[i];
8673             if(fn.call(scope||this, c, i) === true){
8674                 r[r.length] = c;
8675             }
8676         }
8677         return r;
8678     },
8679
8680     /**
8681      * Returns true if the specified column is sortable.
8682      * @param {Number} col The column index
8683      * @return {Boolean}
8684      */
8685     isSortable : function(col){
8686         if(typeof this.config[col].sortable == "undefined"){
8687             return this.defaultSortable;
8688         }
8689         return this.config[col].sortable;
8690     },
8691
8692     /**
8693      * Returns the rendering (formatting) function defined for the column.
8694      * @param {Number} col The column index.
8695      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8696      */
8697     getRenderer : function(col){
8698         if(!this.config[col].renderer){
8699             return Roo.grid.ColumnModel.defaultRenderer;
8700         }
8701         return this.config[col].renderer;
8702     },
8703
8704     /**
8705      * Sets the rendering (formatting) function for a column.
8706      * @param {Number} col The column index
8707      * @param {Function} fn The function to use to process the cell's raw data
8708      * to return HTML markup for the grid view. The render function is called with
8709      * the following parameters:<ul>
8710      * <li>Data value.</li>
8711      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8712      * <li>css A CSS style string to apply to the table cell.</li>
8713      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8714      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8715      * <li>Row index</li>
8716      * <li>Column index</li>
8717      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8718      */
8719     setRenderer : function(col, fn){
8720         this.config[col].renderer = fn;
8721     },
8722
8723     /**
8724      * Returns the width for the specified column.
8725      * @param {Number} col The column index
8726      * @param (optional) {String} gridSize bootstrap width size.
8727      * @return {Number}
8728      */
8729     getColumnWidth : function(col, gridSize)
8730         {
8731                 var cfg = this.config[col];
8732                 
8733                 if (typeof(gridSize) == 'undefined') {
8734                         return cfg.width * 1 || this.defaultWidth;
8735                 }
8736                 if (gridSize === false) { // if we set it..
8737                         return cfg.width || false;
8738                 }
8739                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8740                 
8741                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8742                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8743                                 continue;
8744                         }
8745                         return cfg[ sizes[i] ];
8746                 }
8747                 return 1;
8748                 
8749     },
8750
8751     /**
8752      * Sets the width for a column.
8753      * @param {Number} col The column index
8754      * @param {Number} width The new width
8755      */
8756     setColumnWidth : function(col, width, suppressEvent){
8757         this.config[col].width = width;
8758         this.totalWidth = null;
8759         if(!suppressEvent){
8760              this.fireEvent("widthchange", this, col, width);
8761         }
8762     },
8763
8764     /**
8765      * Returns the total width of all columns.
8766      * @param {Boolean} includeHidden True to include hidden column widths
8767      * @return {Number}
8768      */
8769     getTotalWidth : function(includeHidden){
8770         if(!this.totalWidth){
8771             this.totalWidth = 0;
8772             for(var i = 0, len = this.config.length; i < len; i++){
8773                 if(includeHidden || !this.isHidden(i)){
8774                     this.totalWidth += this.getColumnWidth(i);
8775                 }
8776             }
8777         }
8778         return this.totalWidth;
8779     },
8780
8781     /**
8782      * Returns the header for the specified column.
8783      * @param {Number} col The column index
8784      * @return {String}
8785      */
8786     getColumnHeader : function(col){
8787         return this.config[col].header;
8788     },
8789
8790     /**
8791      * Sets the header for a column.
8792      * @param {Number} col The column index
8793      * @param {String} header The new header
8794      */
8795     setColumnHeader : function(col, header){
8796         this.config[col].header = header;
8797         this.fireEvent("headerchange", this, col, header);
8798     },
8799
8800     /**
8801      * Returns the tooltip for the specified column.
8802      * @param {Number} col The column index
8803      * @return {String}
8804      */
8805     getColumnTooltip : function(col){
8806             return this.config[col].tooltip;
8807     },
8808     /**
8809      * Sets the tooltip for a column.
8810      * @param {Number} col The column index
8811      * @param {String} tooltip The new tooltip
8812      */
8813     setColumnTooltip : function(col, tooltip){
8814             this.config[col].tooltip = tooltip;
8815     },
8816
8817     /**
8818      * Returns the dataIndex for the specified column.
8819      * @param {Number} col The column index
8820      * @return {Number}
8821      */
8822     getDataIndex : function(col){
8823         return this.config[col].dataIndex;
8824     },
8825
8826     /**
8827      * Sets the dataIndex for a column.
8828      * @param {Number} col The column index
8829      * @param {Number} dataIndex The new dataIndex
8830      */
8831     setDataIndex : function(col, dataIndex){
8832         this.config[col].dataIndex = dataIndex;
8833     },
8834
8835     
8836     
8837     /**
8838      * Returns true if the cell is editable.
8839      * @param {Number} colIndex The column index
8840      * @param {Number} rowIndex The row index - this is nto actually used..?
8841      * @return {Boolean}
8842      */
8843     isCellEditable : function(colIndex, rowIndex){
8844         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8845     },
8846
8847     /**
8848      * Returns the editor defined for the cell/column.
8849      * return false or null to disable editing.
8850      * @param {Number} colIndex The column index
8851      * @param {Number} rowIndex The row index
8852      * @return {Object}
8853      */
8854     getCellEditor : function(colIndex, rowIndex){
8855         return this.config[colIndex].editor;
8856     },
8857
8858     /**
8859      * Sets if a column is editable.
8860      * @param {Number} col The column index
8861      * @param {Boolean} editable True if the column is editable
8862      */
8863     setEditable : function(col, editable){
8864         this.config[col].editable = editable;
8865     },
8866
8867
8868     /**
8869      * Returns true if the column is hidden.
8870      * @param {Number} colIndex The column index
8871      * @return {Boolean}
8872      */
8873     isHidden : function(colIndex){
8874         return this.config[colIndex].hidden;
8875     },
8876
8877
8878     /**
8879      * Returns true if the column width cannot be changed
8880      */
8881     isFixed : function(colIndex){
8882         return this.config[colIndex].fixed;
8883     },
8884
8885     /**
8886      * Returns true if the column can be resized
8887      * @return {Boolean}
8888      */
8889     isResizable : function(colIndex){
8890         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8891     },
8892     /**
8893      * Sets if a column is hidden.
8894      * @param {Number} colIndex The column index
8895      * @param {Boolean} hidden True if the column is hidden
8896      */
8897     setHidden : function(colIndex, hidden){
8898         this.config[colIndex].hidden = hidden;
8899         this.totalWidth = null;
8900         this.fireEvent("hiddenchange", this, colIndex, hidden);
8901     },
8902
8903     /**
8904      * Sets the editor for a column.
8905      * @param {Number} col The column index
8906      * @param {Object} editor The editor object
8907      */
8908     setEditor : function(col, editor){
8909         this.config[col].editor = editor;
8910     },
8911     /**
8912      * Add a column (experimental...) - defaults to adding to the end..
8913      * @param {Object} config 
8914     */
8915     addColumn : function(c)
8916     {
8917     
8918         var i = this.config.length;
8919         this.config[i] = c;
8920         
8921         if(typeof c.dataIndex == "undefined"){
8922             c.dataIndex = i;
8923         }
8924         if(typeof c.renderer == "string"){
8925             c.renderer = Roo.util.Format[c.renderer];
8926         }
8927         if(typeof c.id == "undefined"){
8928             c.id = Roo.id();
8929         }
8930         if(c.editor && c.editor.xtype){
8931             c.editor  = Roo.factory(c.editor, Roo.grid);
8932         }
8933         if(c.editor && c.editor.isFormField){
8934             c.editor = new Roo.grid.GridEditor(c.editor);
8935         }
8936         this.lookup[c.id] = c;
8937     }
8938     
8939 });
8940
8941 Roo.grid.ColumnModel.defaultRenderer = function(value)
8942 {
8943     if(typeof value == "object") {
8944         return value;
8945     }
8946         if(typeof value == "string" && value.length < 1){
8947             return "&#160;";
8948         }
8949     
8950         return String.format("{0}", value);
8951 };
8952
8953 // Alias for backwards compatibility
8954 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8955 /*
8956  * Based on:
8957  * Ext JS Library 1.1.1
8958  * Copyright(c) 2006-2007, Ext JS, LLC.
8959  *
8960  * Originally Released Under LGPL - original licence link has changed is not relivant.
8961  *
8962  * Fork - LGPL
8963  * <script type="text/javascript">
8964  */
8965  
8966 /**
8967  * @class Roo.LoadMask
8968  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8969  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8970  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8971  * element's UpdateManager load indicator and will be destroyed after the initial load.
8972  * @constructor
8973  * Create a new LoadMask
8974  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8975  * @param {Object} config The config object
8976  */
8977 Roo.LoadMask = function(el, config){
8978     this.el = Roo.get(el);
8979     Roo.apply(this, config);
8980     if(this.store){
8981         this.store.on('beforeload', this.onBeforeLoad, this);
8982         this.store.on('load', this.onLoad, this);
8983         this.store.on('loadexception', this.onLoadException, this);
8984         this.removeMask = false;
8985     }else{
8986         var um = this.el.getUpdateManager();
8987         um.showLoadIndicator = false; // disable the default indicator
8988         um.on('beforeupdate', this.onBeforeLoad, this);
8989         um.on('update', this.onLoad, this);
8990         um.on('failure', this.onLoad, this);
8991         this.removeMask = true;
8992     }
8993 };
8994
8995 Roo.LoadMask.prototype = {
8996     /**
8997      * @cfg {Boolean} removeMask
8998      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8999      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9000      */
9001     removeMask : false,
9002     /**
9003      * @cfg {String} msg
9004      * The text to display in a centered loading message box (defaults to 'Loading...')
9005      */
9006     msg : 'Loading...',
9007     /**
9008      * @cfg {String} msgCls
9009      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9010      */
9011     msgCls : 'x-mask-loading',
9012
9013     /**
9014      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9015      * @type Boolean
9016      */
9017     disabled: false,
9018
9019     /**
9020      * Disables the mask to prevent it from being displayed
9021      */
9022     disable : function(){
9023        this.disabled = true;
9024     },
9025
9026     /**
9027      * Enables the mask so that it can be displayed
9028      */
9029     enable : function(){
9030         this.disabled = false;
9031     },
9032     
9033     onLoadException : function()
9034     {
9035         Roo.log(arguments);
9036         
9037         if (typeof(arguments[3]) != 'undefined') {
9038             Roo.MessageBox.alert("Error loading",arguments[3]);
9039         } 
9040         /*
9041         try {
9042             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9043                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9044             }   
9045         } catch(e) {
9046             
9047         }
9048         */
9049     
9050         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9051     },
9052     // private
9053     onLoad : function()
9054     {
9055         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9056     },
9057
9058     // private
9059     onBeforeLoad : function(){
9060         if(!this.disabled){
9061             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9062         }
9063     },
9064
9065     // private
9066     destroy : function(){
9067         if(this.store){
9068             this.store.un('beforeload', this.onBeforeLoad, this);
9069             this.store.un('load', this.onLoad, this);
9070             this.store.un('loadexception', this.onLoadException, this);
9071         }else{
9072             var um = this.el.getUpdateManager();
9073             um.un('beforeupdate', this.onBeforeLoad, this);
9074             um.un('update', this.onLoad, this);
9075             um.un('failure', this.onLoad, this);
9076         }
9077     }
9078 };/**
9079  * @class Roo.bootstrap.Table
9080  * @licence LGBL
9081  * @extends Roo.bootstrap.Component
9082  * @children Roo.bootstrap.TableBody
9083  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9084  * Similar to Roo.grid.Grid
9085  * <pre><code>
9086  var table = Roo.factory({
9087     xtype : 'Table',
9088     xns : Roo.bootstrap,
9089     autoSizeColumns: true,
9090     
9091     
9092     store : {
9093         xtype : 'Store',
9094         xns : Roo.data,
9095         remoteSort : true,
9096         sortInfo : { direction : 'ASC', field: 'name' },
9097         proxy : {
9098            xtype : 'HttpProxy',
9099            xns : Roo.data,
9100            method : 'GET',
9101            url : 'https://example.com/some.data.url.json'
9102         },
9103         reader : {
9104            xtype : 'JsonReader',
9105            xns : Roo.data,
9106            fields : [ 'id', 'name', whatever' ],
9107            id : 'id',
9108            root : 'data'
9109         }
9110     },
9111     cm : [
9112         {
9113             xtype : 'ColumnModel',
9114             xns : Roo.grid,
9115             align : 'center',
9116             cursor : 'pointer',
9117             dataIndex : 'is_in_group',
9118             header : "Name",
9119             sortable : true,
9120             renderer : function(v, x , r) {  
9121             
9122                 return String.format("{0}", v)
9123             }
9124             width : 3
9125         } // more columns..
9126     ],
9127     selModel : {
9128         xtype : 'RowSelectionModel',
9129         xns : Roo.bootstrap.Table
9130         // you can add listeners to catch selection change here....
9131     }
9132      
9133
9134  });
9135  // set any options
9136  grid.render(Roo.get("some-div"));
9137 </code></pre>
9138
9139 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9140
9141
9142
9143  *
9144  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9145  * @cfg {Roo.data.Store} store The data store to use
9146  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9147  * 
9148  * @cfg {String} cls table class
9149  *
9150  *
9151  * @cfg {string} empty_results  Text to display for no results 
9152  * @cfg {boolean} striped Should the rows be alternative striped
9153  * @cfg {boolean} bordered Add borders to the table
9154  * @cfg {boolean} hover Add hover highlighting
9155  * @cfg {boolean} condensed Format condensed
9156  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9157  *                also adds table-responsive (see bootstrap docs for details)
9158  * @cfg {Boolean} loadMask (true|false) default false
9159  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9160  * @cfg {Boolean} summaryFooterShow (true|false) generate tfoot for summary, default false
9161  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9162  * @cfg {Boolean} rowSelection (true|false) default false
9163  * @cfg {Boolean} cellSelection (true|false) default false
9164  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9165  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9166  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9167  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9168  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9169  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9170  *
9171  * 
9172  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9173  * 
9174  * @constructor
9175  * Create a new Table
9176  * @param {Object} config The config object
9177  */
9178
9179 Roo.bootstrap.Table = function(config)
9180 {
9181     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9182      
9183     // BC...
9184     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9185     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9186     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9187     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9188     
9189     this.view = this; // compat with grid.
9190     
9191     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9192     if (this.sm) {
9193         this.sm.grid = this;
9194         this.selModel = Roo.factory(this.sm, Roo.grid);
9195         this.sm = this.selModel;
9196         this.sm.xmodule = this.xmodule || false;
9197     }
9198     
9199     if (this.cm && typeof(this.cm.config) == 'undefined') {
9200         this.colModel = new Roo.grid.ColumnModel(this.cm);
9201         this.cm = this.colModel;
9202         this.cm.xmodule = this.xmodule || false;
9203     }
9204     if (this.store) {
9205         this.store= Roo.factory(this.store, Roo.data);
9206         this.ds = this.store;
9207         this.ds.xmodule = this.xmodule || false;
9208          
9209     }
9210     if (this.footer && this.store) {
9211         this.footer.dataSource = this.ds;
9212         this.footer = Roo.factory(this.footer);
9213     }
9214     
9215     /** @private */
9216     this.addEvents({
9217         /**
9218          * @event cellclick
9219          * Fires when a cell is clicked
9220          * @param {Roo.bootstrap.Table} this
9221          * @param {Roo.Element} el
9222          * @param {Number} rowIndex
9223          * @param {Number} columnIndex
9224          * @param {Roo.EventObject} e
9225          */
9226         "cellclick" : true,
9227         /**
9228          * @event celldblclick
9229          * Fires when a cell is double clicked
9230          * @param {Roo.bootstrap.Table} this
9231          * @param {Roo.Element} el
9232          * @param {Number} rowIndex
9233          * @param {Number} columnIndex
9234          * @param {Roo.EventObject} e
9235          */
9236         "celldblclick" : true,
9237         /**
9238          * @event rowclick
9239          * Fires when a row is clicked
9240          * @param {Roo.bootstrap.Table} this
9241          * @param {Roo.Element} el
9242          * @param {Number} rowIndex
9243          * @param {Roo.EventObject} e
9244          */
9245         "rowclick" : true,
9246         /**
9247          * @event rowdblclick
9248          * Fires when a row is double clicked
9249          * @param {Roo.bootstrap.Table} this
9250          * @param {Roo.Element} el
9251          * @param {Number} rowIndex
9252          * @param {Roo.EventObject} e
9253          */
9254         "rowdblclick" : true,
9255         /**
9256          * @event mouseover
9257          * Fires when a mouseover occur
9258          * @param {Roo.bootstrap.Table} this
9259          * @param {Roo.Element} el
9260          * @param {Number} rowIndex
9261          * @param {Number} columnIndex
9262          * @param {Roo.EventObject} e
9263          */
9264         "mouseover" : true,
9265         /**
9266          * @event mouseout
9267          * Fires when a mouseout occur
9268          * @param {Roo.bootstrap.Table} this
9269          * @param {Roo.Element} el
9270          * @param {Number} rowIndex
9271          * @param {Number} columnIndex
9272          * @param {Roo.EventObject} e
9273          */
9274         "mouseout" : true,
9275         /**
9276          * @event rowclass
9277          * Fires when a row is rendered, so you can change add a style to it.
9278          * @param {Roo.bootstrap.Table} this
9279          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9280          */
9281         'rowclass' : true,
9282           /**
9283          * @event rowsrendered
9284          * Fires when all the  rows have been rendered
9285          * @param {Roo.bootstrap.Table} this
9286          */
9287         'rowsrendered' : true,
9288         /**
9289          * @event contextmenu
9290          * The raw contextmenu event for the entire grid.
9291          * @param {Roo.EventObject} e
9292          */
9293         "contextmenu" : true,
9294         /**
9295          * @event rowcontextmenu
9296          * Fires when a row is right clicked
9297          * @param {Roo.bootstrap.Table} this
9298          * @param {Number} rowIndex
9299          * @param {Roo.EventObject} e
9300          */
9301         "rowcontextmenu" : true,
9302         /**
9303          * @event cellcontextmenu
9304          * Fires when a cell is right clicked
9305          * @param {Roo.bootstrap.Table} this
9306          * @param {Number} rowIndex
9307          * @param {Number} cellIndex
9308          * @param {Roo.EventObject} e
9309          */
9310          "cellcontextmenu" : true,
9311          /**
9312          * @event headercontextmenu
9313          * Fires when a header is right clicked
9314          * @param {Roo.bootstrap.Table} this
9315          * @param {Number} columnIndex
9316          * @param {Roo.EventObject} e
9317          */
9318         "headercontextmenu" : true,
9319         /**
9320          * @event mousedown
9321          * The raw mousedown event for the entire grid.
9322          * @param {Roo.EventObject} e
9323          */
9324         "mousedown" : true
9325         
9326     });
9327 };
9328
9329 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9330     
9331     cls: false,
9332     
9333     empty_results : '',
9334     striped : false,
9335     scrollBody : false,
9336     bordered: false,
9337     hover:  false,
9338     condensed : false,
9339     responsive : false,
9340     sm : false,
9341     cm : false,
9342     store : false,
9343     loadMask : false,
9344     footerShow : true,
9345     summaryFooterShow : false,
9346     headerShow : true,
9347     enableColumnResize: true,
9348     disableAutoSize: false,
9349   
9350     rowSelection : false,
9351     cellSelection : false,
9352     layout : false,
9353
9354     minColumnWidth : 50,
9355     
9356     // Roo.Element - the tbody
9357     bodyEl: false,  // <tbody> Roo.Element - thead element    
9358     headEl: false,  // <thead> Roo.Element - thead element
9359     resizeProxy : false, // proxy element for dragging?
9360
9361
9362     
9363     container: false, // used by gridpanel...
9364     
9365     lazyLoad : false,
9366     
9367     CSS : Roo.util.CSS,
9368     
9369     auto_hide_footer : false,
9370     
9371     view: false, // actually points to this..
9372     
9373     getAutoCreate : function()
9374     {
9375         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9376         
9377         cfg = {
9378             tag: 'table',
9379             cls : 'table', 
9380             cn : []
9381         };
9382         // this get's auto added by panel.Grid
9383         if (this.scrollBody) {
9384             cfg.cls += ' table-body-fixed';
9385         }    
9386         if (this.striped) {
9387             cfg.cls += ' table-striped';
9388         }
9389         
9390         if (this.hover) {
9391             cfg.cls += ' table-hover';
9392         }
9393         if (this.bordered) {
9394             cfg.cls += ' table-bordered';
9395         }
9396         if (this.condensed) {
9397             cfg.cls += ' table-condensed';
9398         }
9399         
9400         if (this.responsive) {
9401             cfg.cls += ' table-responsive';
9402         }
9403         
9404         if (this.cls) {
9405             cfg.cls+=  ' ' +this.cls;
9406         }
9407         
9408         
9409         
9410         if (this.layout) {
9411             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9412         }
9413         
9414         if(this.store || this.cm){
9415             if(this.headerShow){
9416                 cfg.cn.push(this.renderHeader());
9417             }
9418             
9419             cfg.cn.push(this.renderBody());
9420             
9421             if(this.footerShow){
9422                 cfg.cn.push(this.renderFooter());
9423             }
9424
9425             if(!this.footerShow && this.summaryFooterShow) {
9426                 cfg.cn.push(this.renderSummaryFooter());
9427             }
9428             // where does this come from?
9429             //cfg.cls+=  ' TableGrid';
9430         }
9431         
9432         return { cn : [ cfg ] };
9433     },
9434     
9435     initEvents : function()
9436     {   
9437         if(!this.store || !this.cm){
9438             return;
9439         }
9440         if (this.selModel) {
9441             this.selModel.initEvents();
9442         }
9443         
9444         
9445         //Roo.log('initEvents with ds!!!!');
9446         
9447         this.bodyEl = this.el.select('tbody', true).first();
9448         this.headEl = this.el.select('thead', true).first();
9449         this.mainFoot = this.el.select('tfoot', true).first();
9450         
9451         
9452         
9453         
9454         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9455             e.on('click', this.sort, this);
9456         }, this);
9457         
9458         
9459         // why is this done????? = it breaks dialogs??
9460         //this.parent().el.setStyle('position', 'relative');
9461         
9462         
9463         if (this.footer) {
9464             this.footer.parentId = this.id;
9465             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9466             
9467             if(this.lazyLoad){
9468                 this.el.select('tfoot tr td').first().addClass('hide');
9469             }
9470         } 
9471         
9472         if(this.loadMask) {
9473             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9474         }
9475         
9476         this.store.on('load', this.onLoad, this);
9477         this.store.on('beforeload', this.onBeforeLoad, this);
9478         this.store.on('update', this.onUpdate, this);
9479         this.store.on('add', this.onAdd, this);
9480         this.store.on("clear", this.clear, this);
9481         
9482         this.el.on("contextmenu", this.onContextMenu, this);
9483         
9484         
9485         this.cm.on("headerchange", this.onHeaderChange, this);
9486         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9487
9488  //?? does bodyEl get replaced on render?
9489         this.bodyEl.on("click", this.onClick, this);
9490         this.bodyEl.on("dblclick", this.onDblClick, this);        
9491         this.bodyEl.on('scroll', this.onBodyScroll, this);
9492
9493         // guessing mainbody will work - this relays usually caught by selmodel at present.
9494         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9495   
9496   
9497         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9498         
9499   
9500         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9501             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9502         }
9503         
9504         this.initCSS();
9505     },
9506     // Compatibility with grid - we implement all the view features at present.
9507     getView : function()
9508     {
9509         return this;
9510     },
9511     
9512     initCSS : function()
9513     {
9514         if(this.disableAutoSize) {
9515             return;
9516         }
9517         
9518         var cm = this.cm, styles = [];
9519         this.CSS.removeStyleSheet(this.id + '-cssrules');
9520         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9521         // we can honour xs/sm/md/xl  as widths...
9522         // we first have to decide what widht we are currently at...
9523         var sz = Roo.getGridSize();
9524         
9525         var total = 0;
9526         var last = -1;
9527         var cols = []; // visable cols.
9528         var total_abs = 0;
9529         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9530             var w = cm.getColumnWidth(i, false);
9531             if(cm.isHidden(i)){
9532                 cols.push( { rel : false, abs : 0 });
9533                 continue;
9534             }
9535             if (w !== false) {
9536                 cols.push( { rel : false, abs : w });
9537                 total_abs += w;
9538                 last = i; // not really..
9539                 continue;
9540             }
9541             var w = cm.getColumnWidth(i, sz);
9542             if (w > 0) {
9543                 last = i
9544             }
9545             total += w;
9546             cols.push( { rel : w, abs : false });
9547         }
9548         
9549         var avail = this.bodyEl.dom.clientWidth - total_abs;
9550         
9551         var unitWidth = Math.floor(avail / total);
9552         var rem = avail - (unitWidth * total);
9553         
9554         var hidden, width, pos = 0 , splithide , left;
9555         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9556             
9557             hidden = 'display:none;';
9558             left = '';
9559             width  = 'width:0px;';
9560             splithide = '';
9561             if(!cm.isHidden(i)){
9562                 hidden = '';
9563                 
9564                 
9565                 // we can honour xs/sm/md/xl ?
9566                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9567                 if (w===0) {
9568                     hidden = 'display:none;';
9569                 }
9570                 // width should return a small number...
9571                 if (i == last) {
9572                     w+=rem; // add the remaining with..
9573                 }
9574                 pos += w;
9575                 left = "left:" + (pos -4) + "px;";
9576                 width = "width:" + w+ "px;";
9577                 
9578             }
9579             if (this.responsive) {
9580                 width = '';
9581                 left = '';
9582                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9583                 splithide = 'display: none;';
9584             }
9585             
9586             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9587             if (this.headEl) {
9588                 if (i == last) {
9589                     splithide = 'display:none;';
9590                 }
9591                 
9592                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9593                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9594                             // this is the popover version..
9595                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9596                 );
9597             }
9598             
9599         }
9600         //Roo.log(styles.join(''));
9601         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9602         
9603     },
9604     
9605     
9606     
9607     onContextMenu : function(e, t)
9608     {
9609         this.processEvent("contextmenu", e);
9610     },
9611     
9612     processEvent : function(name, e)
9613     {
9614         if (name != 'touchstart' ) {
9615             this.fireEvent(name, e);    
9616         }
9617         
9618         var t = e.getTarget();
9619         
9620         var cell = Roo.get(t);
9621         
9622         if(!cell){
9623             return;
9624         }
9625         
9626         if(cell.findParent('tfoot', false, true)){
9627             return;
9628         }
9629         
9630         if(cell.findParent('thead', false, true)){
9631             
9632             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9633                 cell = Roo.get(t).findParent('th', false, true);
9634                 if (!cell) {
9635                     Roo.log("failed to find th in thead?");
9636                     Roo.log(e.getTarget());
9637                     return;
9638                 }
9639             }
9640             
9641             var cellIndex = cell.dom.cellIndex;
9642             
9643             var ename = name == 'touchstart' ? 'click' : name;
9644             this.fireEvent("header" + ename, this, cellIndex, e);
9645             
9646             return;
9647         }
9648         
9649         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9650             cell = Roo.get(t).findParent('td', false, true);
9651             if (!cell) {
9652                 Roo.log("failed to find th in tbody?");
9653                 Roo.log(e.getTarget());
9654                 return;
9655             }
9656         }
9657         
9658         var row = cell.findParent('tr', false, true);
9659         var cellIndex = cell.dom.cellIndex;
9660         var rowIndex = row.dom.rowIndex - 1;
9661         
9662         if(row !== false){
9663             
9664             this.fireEvent("row" + name, this, rowIndex, e);
9665             
9666             if(cell !== false){
9667             
9668                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9669             }
9670         }
9671         
9672     },
9673     
9674     onMouseover : function(e, el)
9675     {
9676         var cell = Roo.get(el);
9677         
9678         if(!cell){
9679             return;
9680         }
9681         
9682         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9683             cell = cell.findParent('td', false, true);
9684         }
9685         
9686         var row = cell.findParent('tr', false, true);
9687         var cellIndex = cell.dom.cellIndex;
9688         var rowIndex = row.dom.rowIndex - 1; // start from 0
9689         
9690         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9691         
9692     },
9693     
9694     onMouseout : function(e, el)
9695     {
9696         var cell = Roo.get(el);
9697         
9698         if(!cell){
9699             return;
9700         }
9701         
9702         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9703             cell = cell.findParent('td', false, true);
9704         }
9705         
9706         var row = cell.findParent('tr', false, true);
9707         var cellIndex = cell.dom.cellIndex;
9708         var rowIndex = row.dom.rowIndex - 1; // start from 0
9709         
9710         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9711         
9712     },
9713     
9714     onClick : function(e, el)
9715     {
9716         var cell = Roo.get(el);
9717         
9718         if(!cell || (!this.cellSelection && !this.rowSelection)){
9719             return;
9720         }
9721         
9722         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9723             cell = cell.findParent('td', false, true);
9724         }
9725         
9726         if(!cell || typeof(cell) == 'undefined'){
9727             return;
9728         }
9729         
9730         var row = cell.findParent('tr', false, true);
9731         
9732         if(!row || typeof(row) == 'undefined'){
9733             return;
9734         }
9735         
9736         var cellIndex = cell.dom.cellIndex;
9737         var rowIndex = this.getRowIndex(row);
9738         
9739         // why??? - should these not be based on SelectionModel?
9740         //if(this.cellSelection){
9741             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9742         //}
9743         
9744         //if(this.rowSelection){
9745             this.fireEvent('rowclick', this, row, rowIndex, e);
9746         //}
9747          
9748     },
9749         
9750     onDblClick : function(e,el)
9751     {
9752         var cell = Roo.get(el);
9753         
9754         if(!cell || (!this.cellSelection && !this.rowSelection)){
9755             return;
9756         }
9757         
9758         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9759             cell = cell.findParent('td', false, true);
9760         }
9761         
9762         if(!cell || typeof(cell) == 'undefined'){
9763             return;
9764         }
9765         
9766         var row = cell.findParent('tr', false, true);
9767         
9768         if(!row || typeof(row) == 'undefined'){
9769             return;
9770         }
9771         
9772         var cellIndex = cell.dom.cellIndex;
9773         var rowIndex = this.getRowIndex(row);
9774         
9775         if(this.cellSelection){
9776             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9777         }
9778         
9779         if(this.rowSelection){
9780             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9781         }
9782     },
9783     findRowIndex : function(el)
9784     {
9785         var cell = Roo.get(el);
9786         if(!cell) {
9787             return false;
9788         }
9789         var row = cell.findParent('tr', false, true);
9790         
9791         if(!row || typeof(row) == 'undefined'){
9792             return false;
9793         }
9794         return this.getRowIndex(row);
9795     },
9796     sort : function(e,el)
9797     {
9798         var col = Roo.get(el);
9799         
9800         if(!col.hasClass('sortable')){
9801             return;
9802         }
9803         
9804         var sort = col.attr('sort');
9805         var dir = 'ASC';
9806         
9807         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9808             dir = 'DESC';
9809         }
9810         
9811         this.store.sortInfo = {field : sort, direction : dir};
9812         
9813         if (this.footer) {
9814             Roo.log("calling footer first");
9815             this.footer.onClick('first');
9816         } else {
9817         
9818             this.store.load({ params : { start : 0 } });
9819         }
9820     },
9821     
9822     renderHeader : function()
9823     {
9824         var header = {
9825             tag: 'thead',
9826             cn : []
9827         };
9828         
9829         var cm = this.cm;
9830         this.totalWidth = 0;
9831         
9832         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9833             
9834             var config = cm.config[i];
9835             
9836             var c = {
9837                 tag: 'th',
9838                 cls : 'x-hcol-' + i,
9839                 style : '',
9840                 
9841                 html: cm.getColumnHeader(i)
9842             };
9843             
9844             var tooltip = cm.getColumnTooltip(i);
9845             if (tooltip) {
9846                 c.tooltip = tooltip;
9847             }
9848             
9849             
9850             var hh = '';
9851             
9852             if(typeof(config.sortable) != 'undefined' && config.sortable){
9853                 c.cls += ' sortable';
9854                 c.html = '<i class="fa"></i>' + c.html;
9855             }
9856             
9857             // could use BS4 hidden-..-down 
9858             
9859             if(typeof(config.lgHeader) != 'undefined'){
9860                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9861             }
9862             
9863             if(typeof(config.mdHeader) != 'undefined'){
9864                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9865             }
9866             
9867             if(typeof(config.smHeader) != 'undefined'){
9868                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9869             }
9870             
9871             if(typeof(config.xsHeader) != 'undefined'){
9872                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9873             }
9874             
9875             if(hh.length){
9876                 c.html = hh;
9877             }
9878             
9879             if(typeof(config.tooltip) != 'undefined'){
9880                 c.tooltip = config.tooltip;
9881             }
9882             
9883             if(typeof(config.colspan) != 'undefined'){
9884                 c.colspan = config.colspan;
9885             }
9886             
9887             // hidden is handled by CSS now
9888             
9889             if(typeof(config.dataIndex) != 'undefined'){
9890                 c.sort = config.dataIndex;
9891             }
9892             
9893            
9894             
9895             if(typeof(config.align) != 'undefined' && config.align.length){
9896                 c.style += ' text-align:' + config.align + ';';
9897             }
9898             
9899             /* width is done in CSS
9900              *if(typeof(config.width) != 'undefined'){
9901                 c.style += ' width:' + config.width + 'px;';
9902                 this.totalWidth += config.width;
9903             } else {
9904                 this.totalWidth += 100; // assume minimum of 100 per column?
9905             }
9906             */
9907             
9908             if(typeof(config.cls) != 'undefined'){
9909                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9910             }
9911             // this is the bit that doesnt reall work at all...
9912             
9913             if (this.responsive) {
9914                  
9915             
9916                 ['xs','sm','md','lg'].map(function(size){
9917                     
9918                     if(typeof(config[size]) == 'undefined'){
9919                         return;
9920                     }
9921                      
9922                     if (!config[size]) { // 0 = hidden
9923                         // BS 4 '0' is treated as hide that column and below.
9924                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9925                         return;
9926                     }
9927                     
9928                     c.cls += ' col-' + size + '-' + config[size] + (
9929                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9930                     );
9931                     
9932                     
9933                 });
9934             }
9935             // at the end?
9936             
9937             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9938             
9939             
9940             
9941             
9942             header.cn.push(c)
9943         }
9944         
9945         return header;
9946     },
9947     
9948     renderBody : function()
9949     {
9950         var body = {
9951             tag: 'tbody',
9952             cn : [
9953                 {
9954                     tag: 'tr',
9955                     cn : [
9956                         {
9957                             tag : 'td',
9958                             colspan :  this.cm.getColumnCount()
9959                         }
9960                     ]
9961                 }
9962             ]
9963         };
9964         
9965         return body;
9966     },
9967     
9968     renderFooter : function()
9969     {
9970         var footer = {
9971             tag: 'tfoot',
9972             cn : [
9973                 {
9974                     tag: 'tr',
9975                     cn : [
9976                         {
9977                             tag : 'td',
9978                             colspan :  this.cm.getColumnCount()
9979                         }
9980                     ]
9981                 }
9982             ]
9983         };
9984         
9985         return footer;
9986     },
9987
9988     renderSummaryFooter : function()
9989     {
9990         var footer = {
9991             tag: 'tfoot',
9992             cn : []
9993         };
9994
9995         var cm = this.cm;
9996         
9997         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9998             
9999             var config = cm.config[i];
10000             
10001             var c = {
10002                 tag: 'td',
10003                 cls : 'x-fcol-' + i,
10004                 style : '',
10005                 
10006                 html: config.summaryFooter
10007             };
10008             
10009             footer.cn.push(c)
10010         }
10011         
10012         return footer;
10013     },
10014     
10015     
10016     
10017     onLoad : function()
10018     {
10019 //        Roo.log('ds onload');
10020         this.clear();
10021         
10022         var _this = this;
10023         var cm = this.cm;
10024         var ds = this.store;
10025         
10026         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10027             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10028             if (_this.store.sortInfo) {
10029                     
10030                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10031                     e.select('i', true).addClass(['fa-arrow-up']);
10032                 }
10033                 
10034                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10035                     e.select('i', true).addClass(['fa-arrow-down']);
10036                 }
10037             }
10038         });
10039         
10040         var tbody =  this.bodyEl;
10041               
10042         if(ds.getCount() > 0){
10043             ds.data.each(function(d,rowIndex){
10044                 var row =  this.renderRow(cm, ds, rowIndex);
10045                 
10046                 tbody.createChild(row);
10047                 
10048                 var _this = this;
10049                 
10050                 if(row.cellObjects.length){
10051                     Roo.each(row.cellObjects, function(r){
10052                         _this.renderCellObject(r);
10053                     })
10054                 }
10055                 
10056             }, this);
10057         } else if (this.empty_results.length) {
10058             this.el.mask(this.empty_results, 'no-spinner');
10059         }
10060         
10061         var tfoot = this.el.select('tfoot', true).first();
10062         
10063         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10064             
10065             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10066             
10067             var total = this.ds.getTotalCount();
10068             
10069             if(this.footer.pageSize < total){
10070                 this.mainFoot.show();
10071             }
10072         }
10073         
10074         Roo.each(this.el.select('tbody td', true).elements, function(e){
10075             e.on('mouseover', _this.onMouseover, _this);
10076         });
10077         
10078         Roo.each(this.el.select('tbody td', true).elements, function(e){
10079             e.on('mouseout', _this.onMouseout, _this);
10080         });
10081         this.fireEvent('rowsrendered', this);
10082         
10083         this.autoSize();
10084         
10085         this.initCSS(); /// resize cols
10086
10087         
10088     },
10089     
10090     
10091     onUpdate : function(ds,record)
10092     {
10093         this.refreshRow(record);
10094         this.autoSize();
10095     },
10096     
10097     onRemove : function(ds, record, index, isUpdate){
10098         if(isUpdate !== true){
10099             this.fireEvent("beforerowremoved", this, index, record);
10100         }
10101         var bt = this.bodyEl.dom;
10102         
10103         var rows = this.el.select('tbody > tr', true).elements;
10104         
10105         if(typeof(rows[index]) != 'undefined'){
10106             bt.removeChild(rows[index].dom);
10107         }
10108         
10109 //        if(bt.rows[index]){
10110 //            bt.removeChild(bt.rows[index]);
10111 //        }
10112         
10113         if(isUpdate !== true){
10114             //this.stripeRows(index);
10115             //this.syncRowHeights(index, index);
10116             //this.layout();
10117             this.fireEvent("rowremoved", this, index, record);
10118         }
10119     },
10120     
10121     onAdd : function(ds, records, rowIndex)
10122     {
10123         //Roo.log('on Add called');
10124         // - note this does not handle multiple adding very well..
10125         var bt = this.bodyEl.dom;
10126         for (var i =0 ; i < records.length;i++) {
10127             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10128             //Roo.log(records[i]);
10129             //Roo.log(this.store.getAt(rowIndex+i));
10130             this.insertRow(this.store, rowIndex + i, false);
10131             return;
10132         }
10133         
10134     },
10135     
10136     
10137     refreshRow : function(record){
10138         var ds = this.store, index;
10139         if(typeof record == 'number'){
10140             index = record;
10141             record = ds.getAt(index);
10142         }else{
10143             index = ds.indexOf(record);
10144             if (index < 0) {
10145                 return; // should not happen - but seems to 
10146             }
10147         }
10148         this.insertRow(ds, index, true);
10149         this.autoSize();
10150         this.onRemove(ds, record, index+1, true);
10151         this.autoSize();
10152         //this.syncRowHeights(index, index);
10153         //this.layout();
10154         this.fireEvent("rowupdated", this, index, record);
10155     },
10156     // private - called by RowSelection
10157     onRowSelect : function(rowIndex){
10158         var row = this.getRowDom(rowIndex);
10159         row.addClass(['bg-info','info']);
10160     },
10161     // private - called by RowSelection
10162     onRowDeselect : function(rowIndex)
10163     {
10164         if (rowIndex < 0) {
10165             return;
10166         }
10167         var row = this.getRowDom(rowIndex);
10168         row.removeClass(['bg-info','info']);
10169     },
10170       /**
10171      * Focuses the specified row.
10172      * @param {Number} row The row index
10173      */
10174     focusRow : function(row)
10175     {
10176         //Roo.log('GridView.focusRow');
10177         var x = this.bodyEl.dom.scrollLeft;
10178         this.focusCell(row, 0, false);
10179         this.bodyEl.dom.scrollLeft = x;
10180
10181     },
10182      /**
10183      * Focuses the specified cell.
10184      * @param {Number} row The row index
10185      * @param {Number} col The column index
10186      * @param {Boolean} hscroll false to disable horizontal scrolling
10187      */
10188     focusCell : function(row, col, hscroll)
10189     {
10190         //Roo.log('GridView.focusCell');
10191         var el = this.ensureVisible(row, col, hscroll);
10192         // not sure what focusEL achives = it's a <a> pos relative 
10193         //this.focusEl.alignTo(el, "tl-tl");
10194         //if(Roo.isGecko){
10195         //    this.focusEl.focus();
10196         //}else{
10197         //    this.focusEl.focus.defer(1, this.focusEl);
10198         //}
10199     },
10200     
10201      /**
10202      * Scrolls the specified cell into view
10203      * @param {Number} row The row index
10204      * @param {Number} col The column index
10205      * @param {Boolean} hscroll false to disable horizontal scrolling
10206      */
10207     ensureVisible : function(row, col, hscroll)
10208     {
10209         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10210         //return null; //disable for testing.
10211         if(typeof row != "number"){
10212             row = row.rowIndex;
10213         }
10214         if(row < 0 && row >= this.ds.getCount()){
10215             return  null;
10216         }
10217         col = (col !== undefined ? col : 0);
10218         var cm = this.cm;
10219         while(cm.isHidden(col)){
10220             col++;
10221         }
10222
10223         var el = this.getCellDom(row, col);
10224         if(!el){
10225             return null;
10226         }
10227         var c = this.bodyEl.dom;
10228
10229         var ctop = parseInt(el.offsetTop, 10);
10230         var cleft = parseInt(el.offsetLeft, 10);
10231         var cbot = ctop + el.offsetHeight;
10232         var cright = cleft + el.offsetWidth;
10233
10234         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10235         var ch = 0; //?? header is not withing the area?
10236         var stop = parseInt(c.scrollTop, 10);
10237         var sleft = parseInt(c.scrollLeft, 10);
10238         var sbot = stop + ch;
10239         var sright = sleft + c.clientWidth;
10240         /*
10241         Roo.log('GridView.ensureVisible:' +
10242                 ' ctop:' + ctop +
10243                 ' c.clientHeight:' + c.clientHeight +
10244                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10245                 ' stop:' + stop +
10246                 ' cbot:' + cbot +
10247                 ' sbot:' + sbot +
10248                 ' ch:' + ch  
10249                 );
10250         */
10251         if(ctop < stop){
10252             c.scrollTop = ctop;
10253             //Roo.log("set scrolltop to ctop DISABLE?");
10254         }else if(cbot > sbot){
10255             //Roo.log("set scrolltop to cbot-ch");
10256             c.scrollTop = cbot-ch;
10257         }
10258
10259         if(hscroll !== false){
10260             if(cleft < sleft){
10261                 c.scrollLeft = cleft;
10262             }else if(cright > sright){
10263                 c.scrollLeft = cright-c.clientWidth;
10264             }
10265         }
10266
10267         return el;
10268     },
10269     
10270     
10271     insertRow : function(dm, rowIndex, isUpdate){
10272         
10273         if(!isUpdate){
10274             this.fireEvent("beforerowsinserted", this, rowIndex);
10275         }
10276             //var s = this.getScrollState();
10277         var row = this.renderRow(this.cm, this.store, rowIndex);
10278         // insert before rowIndex..
10279         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10280         
10281         var _this = this;
10282                 
10283         if(row.cellObjects.length){
10284             Roo.each(row.cellObjects, function(r){
10285                 _this.renderCellObject(r);
10286             })
10287         }
10288             
10289         if(!isUpdate){
10290             this.fireEvent("rowsinserted", this, rowIndex);
10291             //this.syncRowHeights(firstRow, lastRow);
10292             //this.stripeRows(firstRow);
10293             //this.layout();
10294         }
10295         
10296     },
10297     
10298     
10299     getRowDom : function(rowIndex)
10300     {
10301         var rows = this.el.select('tbody > tr', true).elements;
10302         
10303         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10304         
10305     },
10306     getCellDom : function(rowIndex, colIndex)
10307     {
10308         var row = this.getRowDom(rowIndex);
10309         if (row === false) {
10310             return false;
10311         }
10312         var cols = row.select('td', true).elements;
10313         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10314         
10315     },
10316     
10317     // returns the object tree for a tr..
10318   
10319     
10320     renderRow : function(cm, ds, rowIndex) 
10321     {
10322         var d = ds.getAt(rowIndex);
10323         
10324         var row = {
10325             tag : 'tr',
10326             cls : 'x-row-' + rowIndex,
10327             cn : []
10328         };
10329             
10330         var cellObjects = [];
10331         
10332         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10333             var config = cm.config[i];
10334             
10335             var renderer = cm.getRenderer(i);
10336             var value = '';
10337             var id = false;
10338             
10339             if(typeof(renderer) !== 'undefined'){
10340                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10341             }
10342             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10343             // and are rendered into the cells after the row is rendered - using the id for the element.
10344             
10345             if(typeof(value) === 'object'){
10346                 id = Roo.id();
10347                 cellObjects.push({
10348                     container : id,
10349                     cfg : value 
10350                 })
10351             }
10352             
10353             var rowcfg = {
10354                 record: d,
10355                 rowIndex : rowIndex,
10356                 colIndex : i,
10357                 rowClass : ''
10358             };
10359
10360             this.fireEvent('rowclass', this, rowcfg);
10361             
10362             var td = {
10363                 tag: 'td',
10364                 // this might end up displaying HTML?
10365                 // this is too messy... - better to only do it on columsn you know are going to be too long
10366                 //tooltip : (typeof(value) === 'object') ? '' : value,
10367                 cls : rowcfg.rowClass + ' x-col-' + i,
10368                 style: '',
10369                 html: (typeof(value) === 'object') ? '' : value
10370             };
10371             
10372             if (id) {
10373                 td.id = id;
10374             }
10375             
10376             if(typeof(config.colspan) != 'undefined'){
10377                 td.colspan = config.colspan;
10378             }
10379             
10380             
10381             
10382             if(typeof(config.align) != 'undefined' && config.align.length){
10383                 td.style += ' text-align:' + config.align + ';';
10384             }
10385             if(typeof(config.valign) != 'undefined' && config.valign.length){
10386                 td.style += ' vertical-align:' + config.valign + ';';
10387             }
10388             /*
10389             if(typeof(config.width) != 'undefined'){
10390                 td.style += ' width:' +  config.width + 'px;';
10391             }
10392             */
10393             
10394             if(typeof(config.cursor) != 'undefined'){
10395                 td.style += ' cursor:' +  config.cursor + ';';
10396             }
10397             
10398             if(typeof(config.cls) != 'undefined'){
10399                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10400             }
10401             if (this.responsive) {
10402                 ['xs','sm','md','lg'].map(function(size){
10403                     
10404                     if(typeof(config[size]) == 'undefined'){
10405                         return;
10406                     }
10407                     
10408                     
10409                       
10410                     if (!config[size]) { // 0 = hidden
10411                         // BS 4 '0' is treated as hide that column and below.
10412                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10413                         return;
10414                     }
10415                     
10416                     td.cls += ' col-' + size + '-' + config[size] + (
10417                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10418                     );
10419                      
10420     
10421                 });
10422             }
10423             row.cn.push(td);
10424            
10425         }
10426         
10427         row.cellObjects = cellObjects;
10428         
10429         return row;
10430           
10431     },
10432     
10433     
10434     
10435     onBeforeLoad : function()
10436     {
10437         this.el.unmask(); // if needed.
10438     },
10439      /**
10440      * Remove all rows
10441      */
10442     clear : function()
10443     {
10444         this.el.select('tbody', true).first().dom.innerHTML = '';
10445     },
10446     /**
10447      * Show or hide a row.
10448      * @param {Number} rowIndex to show or hide
10449      * @param {Boolean} state hide
10450      */
10451     setRowVisibility : function(rowIndex, state)
10452     {
10453         var bt = this.bodyEl.dom;
10454         
10455         var rows = this.el.select('tbody > tr', true).elements;
10456         
10457         if(typeof(rows[rowIndex]) == 'undefined'){
10458             return;
10459         }
10460         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10461         
10462     },
10463     
10464     
10465     getSelectionModel : function(){
10466         if(!this.selModel){
10467             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10468         }
10469         return this.selModel;
10470     },
10471     /*
10472      * Render the Roo.bootstrap object from renderder
10473      */
10474     renderCellObject : function(r)
10475     {
10476         var _this = this;
10477         
10478         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10479         
10480         var t = r.cfg.render(r.container);
10481         
10482         if(r.cfg.cn){
10483             Roo.each(r.cfg.cn, function(c){
10484                 var child = {
10485                     container: t.getChildContainer(),
10486                     cfg: c
10487                 };
10488                 _this.renderCellObject(child);
10489             })
10490         }
10491     },
10492     /**
10493      * get the Row Index from a dom element.
10494      * @param {Roo.Element} row The row to look for
10495      * @returns {Number} the row
10496      */
10497     getRowIndex : function(row)
10498     {
10499         var rowIndex = -1;
10500         
10501         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10502             if(el != row){
10503                 return;
10504             }
10505             
10506             rowIndex = index;
10507         });
10508         
10509         return rowIndex;
10510     },
10511     /**
10512      * get the header TH element for columnIndex
10513      * @param {Number} columnIndex
10514      * @returns {Roo.Element}
10515      */
10516     getHeaderIndex: function(colIndex)
10517     {
10518         var cols = this.headEl.select('th', true).elements;
10519         return cols[colIndex]; 
10520     },
10521     /**
10522      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10523      * @param {domElement} cell to look for
10524      * @returns {Number} the column
10525      */
10526     getCellIndex : function(cell)
10527     {
10528         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10529         if(id){
10530             return parseInt(id[1], 10);
10531         }
10532         return 0;
10533     },
10534      /**
10535      * Returns the grid's underlying element = used by panel.Grid
10536      * @return {Element} The element
10537      */
10538     getGridEl : function(){
10539         return this.el;
10540     },
10541      /**
10542      * Forces a resize - used by panel.Grid
10543      * @return {Element} The element
10544      */
10545     autoSize : function()
10546     {
10547         if(this.disableAutoSize) {
10548             return;
10549         }
10550         //var ctr = Roo.get(this.container.dom.parentElement);
10551         var ctr = Roo.get(this.el.dom);
10552         
10553         var thd = this.getGridEl().select('thead',true).first();
10554         var tbd = this.getGridEl().select('tbody', true).first();
10555         var tfd = this.getGridEl().select('tfoot', true).first();
10556         
10557         var cw = ctr.getWidth();
10558         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10559         
10560         if (tbd) {
10561             
10562             tbd.setWidth(ctr.getWidth());
10563             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10564             // this needs fixing for various usage - currently only hydra job advers I think..
10565             //tdb.setHeight(
10566             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10567             //); 
10568             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10569             cw -= barsize;
10570         }
10571         cw = Math.max(cw, this.totalWidth);
10572         this.getGridEl().select('tbody tr',true).setWidth(cw);
10573         this.initCSS();
10574         
10575         // resize 'expandable coloumn?
10576         
10577         return; // we doe not have a view in this design..
10578         
10579     },
10580     onBodyScroll: function()
10581     {
10582         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10583         if(this.headEl){
10584             this.headEl.setStyle({
10585                 'position' : 'relative',
10586                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10587             });
10588         }
10589         
10590         if(this.lazyLoad){
10591             
10592             var scrollHeight = this.bodyEl.dom.scrollHeight;
10593             
10594             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10595             
10596             var height = this.bodyEl.getHeight();
10597             
10598             if(scrollHeight - height == scrollTop) {
10599                 
10600                 var total = this.ds.getTotalCount();
10601                 
10602                 if(this.footer.cursor + this.footer.pageSize < total){
10603                     
10604                     this.footer.ds.load({
10605                         params : {
10606                             start : this.footer.cursor + this.footer.pageSize,
10607                             limit : this.footer.pageSize
10608                         },
10609                         add : true
10610                     });
10611                 }
10612             }
10613             
10614         }
10615     },
10616     onColumnSplitterMoved : function(i, diff)
10617     {
10618         this.userResized = true;
10619         
10620         var cm = this.colModel;
10621         
10622         var w = this.getHeaderIndex(i).getWidth() + diff;
10623         
10624         
10625         cm.setColumnWidth(i, w, true);
10626         this.initCSS();
10627         //var cid = cm.getColumnId(i); << not used in this version?
10628        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10629         
10630         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10631         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10632         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10633 */
10634         //this.updateSplitters();
10635         //this.layout(); << ??
10636         this.fireEvent("columnresize", i, w);
10637     },
10638     onHeaderChange : function()
10639     {
10640         var header = this.renderHeader();
10641         var table = this.el.select('table', true).first();
10642         
10643         this.headEl.remove();
10644         this.headEl = table.createChild(header, this.bodyEl, false);
10645         
10646         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10647             e.on('click', this.sort, this);
10648         }, this);
10649         
10650         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10651             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10652         }
10653         
10654     },
10655     
10656     onHiddenChange : function(colModel, colIndex, hidden)
10657     {
10658         /*
10659         this.cm.setHidden()
10660         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10661         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10662         
10663         this.CSS.updateRule(thSelector, "display", "");
10664         this.CSS.updateRule(tdSelector, "display", "");
10665         
10666         if(hidden){
10667             this.CSS.updateRule(thSelector, "display", "none");
10668             this.CSS.updateRule(tdSelector, "display", "none");
10669         }
10670         */
10671         // onload calls initCSS()
10672         this.onHeaderChange();
10673         this.onLoad();
10674     },
10675     
10676     setColumnWidth: function(col_index, width)
10677     {
10678         // width = "md-2 xs-2..."
10679         if(!this.colModel.config[col_index]) {
10680             return;
10681         }
10682         
10683         var w = width.split(" ");
10684         
10685         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10686         
10687         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10688         
10689         
10690         for(var j = 0; j < w.length; j++) {
10691             
10692             if(!w[j]) {
10693                 continue;
10694             }
10695             
10696             var size_cls = w[j].split("-");
10697             
10698             if(!Number.isInteger(size_cls[1] * 1)) {
10699                 continue;
10700             }
10701             
10702             if(!this.colModel.config[col_index][size_cls[0]]) {
10703                 continue;
10704             }
10705             
10706             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10707                 continue;
10708             }
10709             
10710             h_row[0].classList.replace(
10711                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10712                 "col-"+size_cls[0]+"-"+size_cls[1]
10713             );
10714             
10715             for(var i = 0; i < rows.length; i++) {
10716                 
10717                 var size_cls = w[j].split("-");
10718                 
10719                 if(!Number.isInteger(size_cls[1] * 1)) {
10720                     continue;
10721                 }
10722                 
10723                 if(!this.colModel.config[col_index][size_cls[0]]) {
10724                     continue;
10725                 }
10726                 
10727                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10728                     continue;
10729                 }
10730                 
10731                 rows[i].classList.replace(
10732                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10733                     "col-"+size_cls[0]+"-"+size_cls[1]
10734                 );
10735             }
10736             
10737             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10738         }
10739     }
10740 });
10741
10742 // currently only used to find the split on drag.. 
10743 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10744
10745 /**
10746  * @depricated
10747 */
10748 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10749 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10750 /*
10751  * - LGPL
10752  *
10753  * table cell
10754  * 
10755  */
10756
10757 /**
10758  * @class Roo.bootstrap.TableCell
10759  * @extends Roo.bootstrap.Component
10760  * @children Roo.bootstrap.Component
10761  * @parent Roo.bootstrap.TableRow
10762  * Bootstrap TableCell class
10763  * 
10764  * @cfg {String} html cell contain text
10765  * @cfg {String} cls cell class
10766  * @cfg {String} tag cell tag (td|th) default td
10767  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10768  * @cfg {String} align Aligns the content in a cell
10769  * @cfg {String} axis Categorizes cells
10770  * @cfg {String} bgcolor Specifies the background color of a cell
10771  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10772  * @cfg {Number} colspan Specifies the number of columns a cell should span
10773  * @cfg {String} headers Specifies one or more header cells a cell is related to
10774  * @cfg {Number} height Sets the height of a cell
10775  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10776  * @cfg {Number} rowspan Sets the number of rows a cell should span
10777  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10778  * @cfg {String} valign Vertical aligns the content in a cell
10779  * @cfg {Number} width Specifies the width of a cell
10780  * 
10781  * @constructor
10782  * Create a new TableCell
10783  * @param {Object} config The config object
10784  */
10785
10786 Roo.bootstrap.TableCell = function(config){
10787     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10788 };
10789
10790 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10791     
10792     html: false,
10793     cls: false,
10794     tag: false,
10795     abbr: false,
10796     align: false,
10797     axis: false,
10798     bgcolor: false,
10799     charoff: false,
10800     colspan: false,
10801     headers: false,
10802     height: false,
10803     nowrap: false,
10804     rowspan: false,
10805     scope: false,
10806     valign: false,
10807     width: false,
10808     
10809     
10810     getAutoCreate : function(){
10811         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10812         
10813         cfg = {
10814             tag: 'td'
10815         };
10816         
10817         if(this.tag){
10818             cfg.tag = this.tag;
10819         }
10820         
10821         if (this.html) {
10822             cfg.html=this.html
10823         }
10824         if (this.cls) {
10825             cfg.cls=this.cls
10826         }
10827         if (this.abbr) {
10828             cfg.abbr=this.abbr
10829         }
10830         if (this.align) {
10831             cfg.align=this.align
10832         }
10833         if (this.axis) {
10834             cfg.axis=this.axis
10835         }
10836         if (this.bgcolor) {
10837             cfg.bgcolor=this.bgcolor
10838         }
10839         if (this.charoff) {
10840             cfg.charoff=this.charoff
10841         }
10842         if (this.colspan) {
10843             cfg.colspan=this.colspan
10844         }
10845         if (this.headers) {
10846             cfg.headers=this.headers
10847         }
10848         if (this.height) {
10849             cfg.height=this.height
10850         }
10851         if (this.nowrap) {
10852             cfg.nowrap=this.nowrap
10853         }
10854         if (this.rowspan) {
10855             cfg.rowspan=this.rowspan
10856         }
10857         if (this.scope) {
10858             cfg.scope=this.scope
10859         }
10860         if (this.valign) {
10861             cfg.valign=this.valign
10862         }
10863         if (this.width) {
10864             cfg.width=this.width
10865         }
10866         
10867         
10868         return cfg;
10869     }
10870    
10871 });
10872
10873  
10874
10875  /*
10876  * - LGPL
10877  *
10878  * table row
10879  * 
10880  */
10881
10882 /**
10883  * @class Roo.bootstrap.TableRow
10884  * @extends Roo.bootstrap.Component
10885  * @children Roo.bootstrap.TableCell
10886  * @parent Roo.bootstrap.TableBody
10887  * Bootstrap TableRow class
10888  * @cfg {String} cls row class
10889  * @cfg {String} align Aligns the content in a table row
10890  * @cfg {String} bgcolor Specifies a background color for a table row
10891  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10892  * @cfg {String} valign Vertical aligns the content in a table row
10893  * 
10894  * @constructor
10895  * Create a new TableRow
10896  * @param {Object} config The config object
10897  */
10898
10899 Roo.bootstrap.TableRow = function(config){
10900     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10901 };
10902
10903 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10904     
10905     cls: false,
10906     align: false,
10907     bgcolor: false,
10908     charoff: false,
10909     valign: false,
10910     
10911     getAutoCreate : function(){
10912         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10913         
10914         cfg = {
10915             tag: 'tr'
10916         };
10917             
10918         if(this.cls){
10919             cfg.cls = this.cls;
10920         }
10921         if(this.align){
10922             cfg.align = this.align;
10923         }
10924         if(this.bgcolor){
10925             cfg.bgcolor = this.bgcolor;
10926         }
10927         if(this.charoff){
10928             cfg.charoff = this.charoff;
10929         }
10930         if(this.valign){
10931             cfg.valign = this.valign;
10932         }
10933         
10934         return cfg;
10935     }
10936    
10937 });
10938
10939  
10940
10941  /*
10942  * - LGPL
10943  *
10944  * table body
10945  * 
10946  */
10947
10948 /**
10949  * @class Roo.bootstrap.TableBody
10950  * @extends Roo.bootstrap.Component
10951  * @children Roo.bootstrap.TableRow
10952  * @parent Roo.bootstrap.Table
10953  * Bootstrap TableBody class
10954  * @cfg {String} cls element class
10955  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10956  * @cfg {String} align Aligns the content inside the element
10957  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10958  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10959  * 
10960  * @constructor
10961  * Create a new TableBody
10962  * @param {Object} config The config object
10963  */
10964
10965 Roo.bootstrap.TableBody = function(config){
10966     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10967 };
10968
10969 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10970     
10971     cls: false,
10972     tag: false,
10973     align: false,
10974     charoff: false,
10975     valign: false,
10976     
10977     getAutoCreate : function(){
10978         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10979         
10980         cfg = {
10981             tag: 'tbody'
10982         };
10983             
10984         if (this.cls) {
10985             cfg.cls=this.cls
10986         }
10987         if(this.tag){
10988             cfg.tag = this.tag;
10989         }
10990         
10991         if(this.align){
10992             cfg.align = this.align;
10993         }
10994         if(this.charoff){
10995             cfg.charoff = this.charoff;
10996         }
10997         if(this.valign){
10998             cfg.valign = this.valign;
10999         }
11000         
11001         return cfg;
11002     }
11003     
11004     
11005 //    initEvents : function()
11006 //    {
11007 //        
11008 //        if(!this.store){
11009 //            return;
11010 //        }
11011 //        
11012 //        this.store = Roo.factory(this.store, Roo.data);
11013 //        this.store.on('load', this.onLoad, this);
11014 //        
11015 //        this.store.load();
11016 //        
11017 //    },
11018 //    
11019 //    onLoad: function () 
11020 //    {   
11021 //        this.fireEvent('load', this);
11022 //    }
11023 //    
11024 //   
11025 });
11026
11027  
11028
11029  /*
11030  * Based on:
11031  * Ext JS Library 1.1.1
11032  * Copyright(c) 2006-2007, Ext JS, LLC.
11033  *
11034  * Originally Released Under LGPL - original licence link has changed is not relivant.
11035  *
11036  * Fork - LGPL
11037  * <script type="text/javascript">
11038  */
11039
11040 // as we use this in bootstrap.
11041 Roo.namespace('Roo.form');
11042  /**
11043  * @class Roo.form.Action
11044  * Internal Class used to handle form actions
11045  * @constructor
11046  * @param {Roo.form.BasicForm} el The form element or its id
11047  * @param {Object} config Configuration options
11048  */
11049
11050  
11051  
11052 // define the action interface
11053 Roo.form.Action = function(form, options){
11054     this.form = form;
11055     this.options = options || {};
11056 };
11057 /**
11058  * Client Validation Failed
11059  * @const 
11060  */
11061 Roo.form.Action.CLIENT_INVALID = 'client';
11062 /**
11063  * Server Validation Failed
11064  * @const 
11065  */
11066 Roo.form.Action.SERVER_INVALID = 'server';
11067  /**
11068  * Connect to Server Failed
11069  * @const 
11070  */
11071 Roo.form.Action.CONNECT_FAILURE = 'connect';
11072 /**
11073  * Reading Data from Server Failed
11074  * @const 
11075  */
11076 Roo.form.Action.LOAD_FAILURE = 'load';
11077
11078 Roo.form.Action.prototype = {
11079     type : 'default',
11080     failureType : undefined,
11081     response : undefined,
11082     result : undefined,
11083
11084     // interface method
11085     run : function(options){
11086
11087     },
11088
11089     // interface method
11090     success : function(response){
11091
11092     },
11093
11094     // interface method
11095     handleResponse : function(response){
11096
11097     },
11098
11099     // default connection failure
11100     failure : function(response){
11101         
11102         this.response = response;
11103         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11104         this.form.afterAction(this, false);
11105     },
11106
11107     processResponse : function(response){
11108         this.response = response;
11109         if(!response.responseText){
11110             return true;
11111         }
11112         this.result = this.handleResponse(response);
11113         return this.result;
11114     },
11115
11116     // utility functions used internally
11117     getUrl : function(appendParams){
11118         var url = this.options.url || this.form.url || this.form.el.dom.action;
11119         if(appendParams){
11120             var p = this.getParams();
11121             if(p){
11122                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11123             }
11124         }
11125         return url;
11126     },
11127
11128     getMethod : function(){
11129         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11130     },
11131
11132     getParams : function(){
11133         var bp = this.form.baseParams;
11134         var p = this.options.params;
11135         if(p){
11136             if(typeof p == "object"){
11137                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11138             }else if(typeof p == 'string' && bp){
11139                 p += '&' + Roo.urlEncode(bp);
11140             }
11141         }else if(bp){
11142             p = Roo.urlEncode(bp);
11143         }
11144         return p;
11145     },
11146
11147     createCallback : function(){
11148         return {
11149             success: this.success,
11150             failure: this.failure,
11151             scope: this,
11152             timeout: (this.form.timeout*1000),
11153             upload: this.form.fileUpload ? this.success : undefined
11154         };
11155     }
11156 };
11157
11158 Roo.form.Action.Submit = function(form, options){
11159     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11160 };
11161
11162 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11163     type : 'submit',
11164
11165     haveProgress : false,
11166     uploadComplete : false,
11167     
11168     // uploadProgress indicator.
11169     uploadProgress : function()
11170     {
11171         if (!this.form.progressUrl) {
11172             return;
11173         }
11174         
11175         if (!this.haveProgress) {
11176             Roo.MessageBox.progress("Uploading", "Uploading");
11177         }
11178         if (this.uploadComplete) {
11179            Roo.MessageBox.hide();
11180            return;
11181         }
11182         
11183         this.haveProgress = true;
11184    
11185         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11186         
11187         var c = new Roo.data.Connection();
11188         c.request({
11189             url : this.form.progressUrl,
11190             params: {
11191                 id : uid
11192             },
11193             method: 'GET',
11194             success : function(req){
11195                //console.log(data);
11196                 var rdata = false;
11197                 var edata;
11198                 try  {
11199                    rdata = Roo.decode(req.responseText)
11200                 } catch (e) {
11201                     Roo.log("Invalid data from server..");
11202                     Roo.log(edata);
11203                     return;
11204                 }
11205                 if (!rdata || !rdata.success) {
11206                     Roo.log(rdata);
11207                     Roo.MessageBox.alert(Roo.encode(rdata));
11208                     return;
11209                 }
11210                 var data = rdata.data;
11211                 
11212                 if (this.uploadComplete) {
11213                    Roo.MessageBox.hide();
11214                    return;
11215                 }
11216                    
11217                 if (data){
11218                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11219                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11220                     );
11221                 }
11222                 this.uploadProgress.defer(2000,this);
11223             },
11224        
11225             failure: function(data) {
11226                 Roo.log('progress url failed ');
11227                 Roo.log(data);
11228             },
11229             scope : this
11230         });
11231            
11232     },
11233     
11234     
11235     run : function()
11236     {
11237         // run get Values on the form, so it syncs any secondary forms.
11238         this.form.getValues();
11239         
11240         var o = this.options;
11241         var method = this.getMethod();
11242         var isPost = method == 'POST';
11243         if(o.clientValidation === false || this.form.isValid()){
11244             
11245             if (this.form.progressUrl) {
11246                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11247                     (new Date() * 1) + '' + Math.random());
11248                     
11249             } 
11250             
11251             
11252             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11253                 form:this.form.el.dom,
11254                 url:this.getUrl(!isPost),
11255                 method: method,
11256                 params:isPost ? this.getParams() : null,
11257                 isUpload: this.form.fileUpload,
11258                 formData : this.form.formData
11259             }));
11260             
11261             this.uploadProgress();
11262
11263         }else if (o.clientValidation !== false){ // client validation failed
11264             this.failureType = Roo.form.Action.CLIENT_INVALID;
11265             this.form.afterAction(this, false);
11266         }
11267     },
11268
11269     success : function(response)
11270     {
11271         this.uploadComplete= true;
11272         if (this.haveProgress) {
11273             Roo.MessageBox.hide();
11274         }
11275         
11276         
11277         var result = this.processResponse(response);
11278         if(result === true || result.success){
11279             this.form.afterAction(this, true);
11280             return;
11281         }
11282         if(result.errors){
11283             this.form.markInvalid(result.errors);
11284             this.failureType = Roo.form.Action.SERVER_INVALID;
11285         }
11286         this.form.afterAction(this, false);
11287     },
11288     failure : function(response)
11289     {
11290         this.uploadComplete= true;
11291         if (this.haveProgress) {
11292             Roo.MessageBox.hide();
11293         }
11294         
11295         this.response = response;
11296         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11297         this.form.afterAction(this, false);
11298     },
11299     
11300     handleResponse : function(response){
11301         if(this.form.errorReader){
11302             var rs = this.form.errorReader.read(response);
11303             var errors = [];
11304             if(rs.records){
11305                 for(var i = 0, len = rs.records.length; i < len; i++) {
11306                     var r = rs.records[i];
11307                     errors[i] = r.data;
11308                 }
11309             }
11310             if(errors.length < 1){
11311                 errors = null;
11312             }
11313             return {
11314                 success : rs.success,
11315                 errors : errors
11316             };
11317         }
11318         var ret = false;
11319         try {
11320             ret = Roo.decode(response.responseText);
11321         } catch (e) {
11322             ret = {
11323                 success: false,
11324                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11325                 errors : []
11326             };
11327         }
11328         return ret;
11329         
11330     }
11331 });
11332
11333
11334 Roo.form.Action.Load = function(form, options){
11335     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11336     this.reader = this.form.reader;
11337 };
11338
11339 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11340     type : 'load',
11341
11342     run : function(){
11343         
11344         Roo.Ajax.request(Roo.apply(
11345                 this.createCallback(), {
11346                     method:this.getMethod(),
11347                     url:this.getUrl(false),
11348                     params:this.getParams()
11349         }));
11350     },
11351
11352     success : function(response){
11353         
11354         var result = this.processResponse(response);
11355         if(result === true || !result.success || !result.data){
11356             this.failureType = Roo.form.Action.LOAD_FAILURE;
11357             this.form.afterAction(this, false);
11358             return;
11359         }
11360         this.form.clearInvalid();
11361         this.form.setValues(result.data);
11362         this.form.afterAction(this, true);
11363     },
11364
11365     handleResponse : function(response){
11366         if(this.form.reader){
11367             var rs = this.form.reader.read(response);
11368             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11369             return {
11370                 success : rs.success,
11371                 data : data
11372             };
11373         }
11374         return Roo.decode(response.responseText);
11375     }
11376 });
11377
11378 Roo.form.Action.ACTION_TYPES = {
11379     'load' : Roo.form.Action.Load,
11380     'submit' : Roo.form.Action.Submit
11381 };/*
11382  * - LGPL
11383  *
11384  * form
11385  *
11386  */
11387
11388 /**
11389  * @class Roo.bootstrap.form.Form
11390  * @extends Roo.bootstrap.Component
11391  * @children Roo.bootstrap.Component
11392  * Bootstrap Form class
11393  * @cfg {String} method  GET | POST (default POST)
11394  * @cfg {String} labelAlign top | left (default top)
11395  * @cfg {String} align left  | right - for navbars
11396  * @cfg {Boolean} loadMask load mask when submit (default true)
11397
11398  *
11399  * @constructor
11400  * Create a new Form
11401  * @param {Object} config The config object
11402  */
11403
11404
11405 Roo.bootstrap.form.Form = function(config){
11406     
11407     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11408     
11409     Roo.bootstrap.form.Form.popover.apply();
11410     
11411     this.addEvents({
11412         /**
11413          * @event clientvalidation
11414          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11415          * @param {Form} this
11416          * @param {Boolean} valid true if the form has passed client-side validation
11417          */
11418         clientvalidation: true,
11419         /**
11420          * @event beforeaction
11421          * Fires before any action is performed. Return false to cancel the action.
11422          * @param {Form} this
11423          * @param {Action} action The action to be performed
11424          */
11425         beforeaction: true,
11426         /**
11427          * @event actionfailed
11428          * Fires when an action fails.
11429          * @param {Form} this
11430          * @param {Action} action The action that failed
11431          */
11432         actionfailed : true,
11433         /**
11434          * @event actioncomplete
11435          * Fires when an action is completed.
11436          * @param {Form} this
11437          * @param {Action} action The action that completed
11438          */
11439         actioncomplete : true
11440     });
11441 };
11442
11443 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11444
11445      /**
11446      * @cfg {String} method
11447      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11448      */
11449     method : 'POST',
11450     /**
11451      * @cfg {String} url
11452      * The URL to use for form actions if one isn't supplied in the action options.
11453      */
11454     /**
11455      * @cfg {Boolean} fileUpload
11456      * Set to true if this form is a file upload.
11457      */
11458
11459     /**
11460      * @cfg {Object} baseParams
11461      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11462      */
11463
11464     /**
11465      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11466      */
11467     timeout: 30,
11468     /**
11469      * @cfg {Sting} align (left|right) for navbar forms
11470      */
11471     align : 'left',
11472
11473     // private
11474     activeAction : null,
11475
11476     /**
11477      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11478      * element by passing it or its id or mask the form itself by passing in true.
11479      * @type Mixed
11480      */
11481     waitMsgTarget : false,
11482
11483     loadMask : true,
11484     
11485     /**
11486      * @cfg {Boolean} errorMask (true|false) default false
11487      */
11488     errorMask : false,
11489     
11490     /**
11491      * @cfg {Number} maskOffset Default 100
11492      */
11493     maskOffset : 100,
11494     
11495     /**
11496      * @cfg {Boolean} maskBody
11497      */
11498     maskBody : false,
11499
11500     getAutoCreate : function(){
11501
11502         var cfg = {
11503             tag: 'form',
11504             method : this.method || 'POST',
11505             id : this.id || Roo.id(),
11506             cls : ''
11507         };
11508         if (this.parent().xtype.match(/^Nav/)) {
11509             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11510
11511         }
11512
11513         if (this.labelAlign == 'left' ) {
11514             cfg.cls += ' form-horizontal';
11515         }
11516
11517
11518         return cfg;
11519     },
11520     initEvents : function()
11521     {
11522         this.el.on('submit', this.onSubmit, this);
11523         // this was added as random key presses on the form where triggering form submit.
11524         this.el.on('keypress', function(e) {
11525             if (e.getCharCode() != 13) {
11526                 return true;
11527             }
11528             // we might need to allow it for textareas.. and some other items.
11529             // check e.getTarget().
11530
11531             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11532                 return true;
11533             }
11534
11535             Roo.log("keypress blocked");
11536
11537             e.preventDefault();
11538             return false;
11539         });
11540         
11541     },
11542     // private
11543     onSubmit : function(e){
11544         e.stopEvent();
11545     },
11546
11547      /**
11548      * Returns true if client-side validation on the form is successful.
11549      * @return Boolean
11550      */
11551     isValid : function(){
11552         var items = this.getItems();
11553         var valid = true;
11554         var target = false;
11555         
11556         items.each(function(f){
11557             
11558             if(f.validate()){
11559                 return;
11560             }
11561             
11562             Roo.log('invalid field: ' + f.name);
11563             
11564             valid = false;
11565
11566             if(!target && f.el.isVisible(true)){
11567                 target = f;
11568             }
11569            
11570         });
11571         
11572         if(this.errorMask && !valid){
11573             Roo.bootstrap.form.Form.popover.mask(this, target);
11574         }
11575         
11576         return valid;
11577     },
11578     
11579     /**
11580      * Returns true if any fields in this form have changed since their original load.
11581      * @return Boolean
11582      */
11583     isDirty : function(){
11584         var dirty = false;
11585         var items = this.getItems();
11586         items.each(function(f){
11587            if(f.isDirty()){
11588                dirty = true;
11589                return false;
11590            }
11591            return true;
11592         });
11593         return dirty;
11594     },
11595      /**
11596      * Performs a predefined action (submit or load) or custom actions you define on this form.
11597      * @param {String} actionName The name of the action type
11598      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11599      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11600      * accept other config options):
11601      * <pre>
11602 Property          Type             Description
11603 ----------------  ---------------  ----------------------------------------------------------------------------------
11604 url               String           The url for the action (defaults to the form's url)
11605 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11606 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11607 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11608                                    validate the form on the client (defaults to false)
11609      * </pre>
11610      * @return {BasicForm} this
11611      */
11612     doAction : function(action, options){
11613         if(typeof action == 'string'){
11614             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11615         }
11616         if(this.fireEvent('beforeaction', this, action) !== false){
11617             this.beforeAction(action);
11618             action.run.defer(100, action);
11619         }
11620         return this;
11621     },
11622
11623     // private
11624     beforeAction : function(action){
11625         var o = action.options;
11626         
11627         if(this.loadMask){
11628             
11629             if(this.maskBody){
11630                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11631             } else {
11632                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11633             }
11634         }
11635         // not really supported yet.. ??
11636
11637         //if(this.waitMsgTarget === true){
11638         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11639         //}else if(this.waitMsgTarget){
11640         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11641         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11642         //}else {
11643         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11644        // }
11645
11646     },
11647
11648     // private
11649     afterAction : function(action, success){
11650         this.activeAction = null;
11651         var o = action.options;
11652
11653         if(this.loadMask){
11654             
11655             if(this.maskBody){
11656                 Roo.get(document.body).unmask();
11657             } else {
11658                 this.el.unmask();
11659             }
11660         }
11661         
11662         //if(this.waitMsgTarget === true){
11663 //            this.el.unmask();
11664         //}else if(this.waitMsgTarget){
11665         //    this.waitMsgTarget.unmask();
11666         //}else{
11667         //    Roo.MessageBox.updateProgress(1);
11668         //    Roo.MessageBox.hide();
11669        // }
11670         //
11671         if(success){
11672             if(o.reset){
11673                 this.reset();
11674             }
11675             Roo.callback(o.success, o.scope, [this, action]);
11676             this.fireEvent('actioncomplete', this, action);
11677
11678         }else{
11679
11680             // failure condition..
11681             // we have a scenario where updates need confirming.
11682             // eg. if a locking scenario exists..
11683             // we look for { errors : { needs_confirm : true }} in the response.
11684             if (
11685                 (typeof(action.result) != 'undefined')  &&
11686                 (typeof(action.result.errors) != 'undefined')  &&
11687                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11688            ){
11689                 var _t = this;
11690                 Roo.log("not supported yet");
11691                  /*
11692
11693                 Roo.MessageBox.confirm(
11694                     "Change requires confirmation",
11695                     action.result.errorMsg,
11696                     function(r) {
11697                         if (r != 'yes') {
11698                             return;
11699                         }
11700                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11701                     }
11702
11703                 );
11704                 */
11705
11706
11707                 return;
11708             }
11709
11710             Roo.callback(o.failure, o.scope, [this, action]);
11711             // show an error message if no failed handler is set..
11712             if (!this.hasListener('actionfailed')) {
11713                 Roo.log("need to add dialog support");
11714                 /*
11715                 Roo.MessageBox.alert("Error",
11716                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11717                         action.result.errorMsg :
11718                         "Saving Failed, please check your entries or try again"
11719                 );
11720                 */
11721             }
11722
11723             this.fireEvent('actionfailed', this, action);
11724         }
11725
11726     },
11727     /**
11728      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11729      * @param {String} id The value to search for
11730      * @return Field
11731      */
11732     findField : function(id){
11733         var items = this.getItems();
11734         var field = items.get(id);
11735         if(!field){
11736              items.each(function(f){
11737                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11738                     field = f;
11739                     return false;
11740                 }
11741                 return true;
11742             });
11743         }
11744         return field || null;
11745     },
11746      /**
11747      * Mark fields in this form invalid in bulk.
11748      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11749      * @return {BasicForm} this
11750      */
11751     markInvalid : function(errors){
11752         if(errors instanceof Array){
11753             for(var i = 0, len = errors.length; i < len; i++){
11754                 var fieldError = errors[i];
11755                 var f = this.findField(fieldError.id);
11756                 if(f){
11757                     f.markInvalid(fieldError.msg);
11758                 }
11759             }
11760         }else{
11761             var field, id;
11762             for(id in errors){
11763                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11764                     field.markInvalid(errors[id]);
11765                 }
11766             }
11767         }
11768         //Roo.each(this.childForms || [], function (f) {
11769         //    f.markInvalid(errors);
11770         //});
11771
11772         return this;
11773     },
11774
11775     /**
11776      * Set values for fields in this form in bulk.
11777      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11778      * @return {BasicForm} this
11779      */
11780     setValues : function(values){
11781         if(values instanceof Array){ // array of objects
11782             for(var i = 0, len = values.length; i < len; i++){
11783                 var v = values[i];
11784                 var f = this.findField(v.id);
11785                 if(f){
11786                     f.setValue(v.value);
11787                     if(this.trackResetOnLoad){
11788                         f.originalValue = f.getValue();
11789                     }
11790                 }
11791             }
11792         }else{ // object hash
11793             var field, id;
11794             for(id in values){
11795                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11796
11797                     if (field.setFromData &&
11798                         field.valueField &&
11799                         field.displayField &&
11800                         // combos' with local stores can
11801                         // be queried via setValue()
11802                         // to set their value..
11803                         (field.store && !field.store.isLocal)
11804                         ) {
11805                         // it's a combo
11806                         var sd = { };
11807                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11808                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11809                         field.setFromData(sd);
11810
11811                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11812                         
11813                         field.setFromData(values);
11814                         
11815                     } else {
11816                         field.setValue(values[id]);
11817                     }
11818
11819
11820                     if(this.trackResetOnLoad){
11821                         field.originalValue = field.getValue();
11822                     }
11823                 }
11824             }
11825         }
11826
11827         //Roo.each(this.childForms || [], function (f) {
11828         //    f.setValues(values);
11829         //});
11830
11831         return this;
11832     },
11833
11834     /**
11835      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11836      * they are returned as an array.
11837      * @param {Boolean} asString
11838      * @return {Object}
11839      */
11840     getValues : function(asString){
11841         //if (this.childForms) {
11842             // copy values from the child forms
11843         //    Roo.each(this.childForms, function (f) {
11844         //        this.setValues(f.getValues());
11845         //    }, this);
11846         //}
11847
11848
11849
11850         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11851         if(asString === true){
11852             return fs;
11853         }
11854         return Roo.urlDecode(fs);
11855     },
11856
11857     /**
11858      * Returns the fields in this form as an object with key/value pairs.
11859      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11860      * @return {Object}
11861      */
11862     getFieldValues : function(with_hidden)
11863     {
11864         var items = this.getItems();
11865         var ret = {};
11866         items.each(function(f){
11867             
11868             if (!f.getName()) {
11869                 return;
11870             }
11871             
11872             var v = f.getValue();
11873             
11874             if (f.inputType =='radio') {
11875                 if (typeof(ret[f.getName()]) == 'undefined') {
11876                     ret[f.getName()] = ''; // empty..
11877                 }
11878
11879                 if (!f.el.dom.checked) {
11880                     return;
11881
11882                 }
11883                 v = f.el.dom.value;
11884
11885             }
11886             
11887             if(f.xtype == 'MoneyField'){
11888                 ret[f.currencyName] = f.getCurrency();
11889             }
11890
11891             // not sure if this supported any more..
11892             if ((typeof(v) == 'object') && f.getRawValue) {
11893                 v = f.getRawValue() ; // dates..
11894             }
11895             // combo boxes where name != hiddenName...
11896             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11897                 ret[f.name] = f.getRawValue();
11898             }
11899             ret[f.getName()] = v;
11900         });
11901
11902         return ret;
11903     },
11904
11905     /**
11906      * Clears all invalid messages in this form.
11907      * @return {BasicForm} this
11908      */
11909     clearInvalid : function(){
11910         var items = this.getItems();
11911
11912         items.each(function(f){
11913            f.clearInvalid();
11914         });
11915
11916         return this;
11917     },
11918
11919     /**
11920      * Resets this form.
11921      * @return {BasicForm} this
11922      */
11923     reset : function(){
11924         var items = this.getItems();
11925         items.each(function(f){
11926             f.reset();
11927         });
11928
11929         Roo.each(this.childForms || [], function (f) {
11930             f.reset();
11931         });
11932
11933
11934         return this;
11935     },
11936     
11937     getItems : function()
11938     {
11939         var r=new Roo.util.MixedCollection(false, function(o){
11940             return o.id || (o.id = Roo.id());
11941         });
11942         var iter = function(el) {
11943             if (el.inputEl) {
11944                 r.add(el);
11945             }
11946             if (!el.items) {
11947                 return;
11948             }
11949             Roo.each(el.items,function(e) {
11950                 iter(e);
11951             });
11952         };
11953
11954         iter(this);
11955         return r;
11956     },
11957     
11958     hideFields : function(items)
11959     {
11960         Roo.each(items, function(i){
11961             
11962             var f = this.findField(i);
11963             
11964             if(!f){
11965                 return;
11966             }
11967             
11968             f.hide();
11969             
11970         }, this);
11971     },
11972     
11973     showFields : function(items)
11974     {
11975         Roo.each(items, function(i){
11976             
11977             var f = this.findField(i);
11978             
11979             if(!f){
11980                 return;
11981             }
11982             
11983             f.show();
11984             
11985         }, this);
11986     }
11987
11988 });
11989
11990 Roo.apply(Roo.bootstrap.form.Form, {
11991     
11992     popover : {
11993         
11994         padding : 5,
11995         
11996         isApplied : false,
11997         
11998         isMasked : false,
11999         
12000         form : false,
12001         
12002         target : false,
12003         
12004         toolTip : false,
12005         
12006         intervalID : false,
12007         
12008         maskEl : false,
12009         
12010         apply : function()
12011         {
12012             if(this.isApplied){
12013                 return;
12014             }
12015             
12016             this.maskEl = {
12017                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12018                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12019                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12020                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12021             };
12022             
12023             this.maskEl.top.enableDisplayMode("block");
12024             this.maskEl.left.enableDisplayMode("block");
12025             this.maskEl.bottom.enableDisplayMode("block");
12026             this.maskEl.right.enableDisplayMode("block");
12027             
12028             this.toolTip = new Roo.bootstrap.Tooltip({
12029                 cls : 'roo-form-error-popover',
12030                 alignment : {
12031                     'left' : ['r-l', [-2,0], 'right'],
12032                     'right' : ['l-r', [2,0], 'left'],
12033                     'bottom' : ['tl-bl', [0,2], 'top'],
12034                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12035                 }
12036             });
12037             
12038             this.toolTip.render(Roo.get(document.body));
12039
12040             this.toolTip.el.enableDisplayMode("block");
12041             
12042             Roo.get(document.body).on('click', function(){
12043                 this.unmask();
12044             }, this);
12045             
12046             Roo.get(document.body).on('touchstart', function(){
12047                 this.unmask();
12048             }, this);
12049             
12050             this.isApplied = true
12051         },
12052         
12053         mask : function(form, target)
12054         {
12055             this.form = form;
12056             
12057             this.target = target;
12058             
12059             if(!this.form.errorMask || !target.el){
12060                 return;
12061             }
12062             
12063             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12064             
12065             Roo.log(scrollable);
12066             
12067             var ot = this.target.el.calcOffsetsTo(scrollable);
12068             
12069             var scrollTo = ot[1] - this.form.maskOffset;
12070             
12071             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12072             
12073             scrollable.scrollTo('top', scrollTo);
12074             
12075             var box = this.target.el.getBox();
12076             Roo.log(box);
12077             var zIndex = Roo.bootstrap.Modal.zIndex++;
12078
12079             
12080             this.maskEl.top.setStyle('position', 'absolute');
12081             this.maskEl.top.setStyle('z-index', zIndex);
12082             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12083             this.maskEl.top.setLeft(0);
12084             this.maskEl.top.setTop(0);
12085             this.maskEl.top.show();
12086             
12087             this.maskEl.left.setStyle('position', 'absolute');
12088             this.maskEl.left.setStyle('z-index', zIndex);
12089             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12090             this.maskEl.left.setLeft(0);
12091             this.maskEl.left.setTop(box.y - this.padding);
12092             this.maskEl.left.show();
12093
12094             this.maskEl.bottom.setStyle('position', 'absolute');
12095             this.maskEl.bottom.setStyle('z-index', zIndex);
12096             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12097             this.maskEl.bottom.setLeft(0);
12098             this.maskEl.bottom.setTop(box.bottom + this.padding);
12099             this.maskEl.bottom.show();
12100
12101             this.maskEl.right.setStyle('position', 'absolute');
12102             this.maskEl.right.setStyle('z-index', zIndex);
12103             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12104             this.maskEl.right.setLeft(box.right + this.padding);
12105             this.maskEl.right.setTop(box.y - this.padding);
12106             this.maskEl.right.show();
12107
12108             this.toolTip.bindEl = this.target.el;
12109
12110             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12111
12112             var tip = this.target.blankText;
12113
12114             if(this.target.getValue() !== '' ) {
12115                 
12116                 if (this.target.invalidText.length) {
12117                     tip = this.target.invalidText;
12118                 } else if (this.target.regexText.length){
12119                     tip = this.target.regexText;
12120                 }
12121             }
12122
12123             this.toolTip.show(tip);
12124
12125             this.intervalID = window.setInterval(function() {
12126                 Roo.bootstrap.form.Form.popover.unmask();
12127             }, 10000);
12128
12129             window.onwheel = function(){ return false;};
12130             
12131             (function(){ this.isMasked = true; }).defer(500, this);
12132             
12133         },
12134         
12135         unmask : function()
12136         {
12137             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12138                 return;
12139             }
12140             
12141             this.maskEl.top.setStyle('position', 'absolute');
12142             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12143             this.maskEl.top.hide();
12144
12145             this.maskEl.left.setStyle('position', 'absolute');
12146             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12147             this.maskEl.left.hide();
12148
12149             this.maskEl.bottom.setStyle('position', 'absolute');
12150             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12151             this.maskEl.bottom.hide();
12152
12153             this.maskEl.right.setStyle('position', 'absolute');
12154             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12155             this.maskEl.right.hide();
12156             
12157             this.toolTip.hide();
12158             
12159             this.toolTip.el.hide();
12160             
12161             window.onwheel = function(){ return true;};
12162             
12163             if(this.intervalID){
12164                 window.clearInterval(this.intervalID);
12165                 this.intervalID = false;
12166             }
12167             
12168             this.isMasked = false;
12169             
12170         }
12171         
12172     }
12173     
12174 });
12175
12176 /*
12177  * Based on:
12178  * Ext JS Library 1.1.1
12179  * Copyright(c) 2006-2007, Ext JS, LLC.
12180  *
12181  * Originally Released Under LGPL - original licence link has changed is not relivant.
12182  *
12183  * Fork - LGPL
12184  * <script type="text/javascript">
12185  */
12186 /**
12187  * @class Roo.form.VTypes
12188  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12189  * @static
12190  */
12191 Roo.form.VTypes = function(){
12192     // closure these in so they are only created once.
12193     var alpha = /^[a-zA-Z_]+$/;
12194     var alphanum = /^[a-zA-Z0-9_]+$/;
12195     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12196     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12197
12198     // All these messages and functions are configurable
12199     return {
12200         /**
12201          * The function used to validate email addresses
12202          * @param {String} value The email address
12203          */
12204         email : function(v){
12205             return email.test(v);
12206         },
12207         /**
12208          * The error text to display when the email validation function returns false
12209          * @type String
12210          */
12211         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12212         /**
12213          * The keystroke filter mask to be applied on email input
12214          * @type RegExp
12215          */
12216         emailMask : /[a-z0-9_\.\-@]/i,
12217
12218         /**
12219          * The function used to validate URLs
12220          * @param {String} value The URL
12221          */
12222         url : function(v){
12223             return url.test(v);
12224         },
12225         /**
12226          * The error text to display when the url validation function returns false
12227          * @type String
12228          */
12229         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12230         
12231         /**
12232          * The function used to validate alpha values
12233          * @param {String} value The value
12234          */
12235         alpha : function(v){
12236             return alpha.test(v);
12237         },
12238         /**
12239          * The error text to display when the alpha validation function returns false
12240          * @type String
12241          */
12242         alphaText : 'This field should only contain letters and _',
12243         /**
12244          * The keystroke filter mask to be applied on alpha input
12245          * @type RegExp
12246          */
12247         alphaMask : /[a-z_]/i,
12248
12249         /**
12250          * The function used to validate alphanumeric values
12251          * @param {String} value The value
12252          */
12253         alphanum : function(v){
12254             return alphanum.test(v);
12255         },
12256         /**
12257          * The error text to display when the alphanumeric validation function returns false
12258          * @type String
12259          */
12260         alphanumText : 'This field should only contain letters, numbers and _',
12261         /**
12262          * The keystroke filter mask to be applied on alphanumeric input
12263          * @type RegExp
12264          */
12265         alphanumMask : /[a-z0-9_]/i
12266     };
12267 }();/*
12268  * - LGPL
12269  *
12270  * Input
12271  * 
12272  */
12273
12274 /**
12275  * @class Roo.bootstrap.form.Input
12276  * @extends Roo.bootstrap.Component
12277  * Bootstrap Input class
12278  * @cfg {Boolean} disabled is it disabled
12279  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12280  * @cfg {String} name name of the input
12281  * @cfg {string} fieldLabel - the label associated
12282  * @cfg {string} placeholder - placeholder to put in text.
12283  * @cfg {string} before - input group add on before
12284  * @cfg {string} after - input group add on after
12285  * @cfg {string} size - (lg|sm) or leave empty..
12286  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12287  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12288  * @cfg {Number} md colspan out of 12 for computer-sized screens
12289  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12290  * @cfg {string} value default value of the input
12291  * @cfg {Number} labelWidth set the width of label 
12292  * @cfg {Number} labellg set the width of label (1-12)
12293  * @cfg {Number} labelmd set the width of label (1-12)
12294  * @cfg {Number} labelsm set the width of label (1-12)
12295  * @cfg {Number} labelxs set the width of label (1-12)
12296  * @cfg {String} labelAlign (top|left)
12297  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12298  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12299  * @cfg {String} indicatorpos (left|right) default left
12300  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12301  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12302  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12303  * @cfg {Roo.bootstrap.Button} before Button to show before
12304  * @cfg {Roo.bootstrap.Button} afterButton to show before
12305  * @cfg {String} align (left|center|right) Default left
12306  * @cfg {Boolean} forceFeedback (true|false) Default false
12307  * 
12308  * @constructor
12309  * Create a new Input
12310  * @param {Object} config The config object
12311  */
12312
12313 Roo.bootstrap.form.Input = function(config){
12314     
12315     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12316     
12317     this.addEvents({
12318         /**
12319          * @event focus
12320          * Fires when this field receives input focus.
12321          * @param {Roo.form.Field} this
12322          */
12323         focus : true,
12324         /**
12325          * @event blur
12326          * Fires when this field loses input focus.
12327          * @param {Roo.form.Field} this
12328          */
12329         blur : true,
12330         /**
12331          * @event specialkey
12332          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12333          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12334          * @param {Roo.form.Field} this
12335          * @param {Roo.EventObject} e The event object
12336          */
12337         specialkey : true,
12338         /**
12339          * @event change
12340          * Fires just before the field blurs if the field value has changed.
12341          * @param {Roo.form.Field} this
12342          * @param {Mixed} newValue The new value
12343          * @param {Mixed} oldValue The original value
12344          */
12345         change : true,
12346         /**
12347          * @event invalid
12348          * Fires after the field has been marked as invalid.
12349          * @param {Roo.form.Field} this
12350          * @param {String} msg The validation message
12351          */
12352         invalid : true,
12353         /**
12354          * @event valid
12355          * Fires after the field has been validated with no errors.
12356          * @param {Roo.form.Field} this
12357          */
12358         valid : true,
12359          /**
12360          * @event keyup
12361          * Fires after the key up
12362          * @param {Roo.form.Field} this
12363          * @param {Roo.EventObject}  e The event Object
12364          */
12365         keyup : true,
12366         /**
12367          * @event paste
12368          * Fires after the user pastes into input
12369          * @param {Roo.form.Field} this
12370          * @param {Roo.EventObject}  e The event Object
12371          */
12372         paste : true
12373     });
12374 };
12375
12376 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12377      /**
12378      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12379       automatic validation (defaults to "keyup").
12380      */
12381     validationEvent : "keyup",
12382      /**
12383      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12384      */
12385     validateOnBlur : true,
12386     /**
12387      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12388      */
12389     validationDelay : 250,
12390      /**
12391      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12392      */
12393     focusClass : "x-form-focus",  // not needed???
12394     
12395        
12396     /**
12397      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12398      */
12399     invalidClass : "has-warning",
12400     
12401     /**
12402      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12403      */
12404     validClass : "has-success",
12405     
12406     /**
12407      * @cfg {Boolean} hasFeedback (true|false) default true
12408      */
12409     hasFeedback : true,
12410     
12411     /**
12412      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12413      */
12414     invalidFeedbackClass : "glyphicon-warning-sign",
12415     
12416     /**
12417      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12418      */
12419     validFeedbackClass : "glyphicon-ok",
12420     
12421     /**
12422      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12423      */
12424     selectOnFocus : false,
12425     
12426      /**
12427      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12428      */
12429     maskRe : null,
12430        /**
12431      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12432      */
12433     vtype : null,
12434     
12435       /**
12436      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12437      */
12438     disableKeyFilter : false,
12439     
12440        /**
12441      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12442      */
12443     disabled : false,
12444      /**
12445      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12446      */
12447     allowBlank : true,
12448     /**
12449      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12450      */
12451     blankText : "Please complete this mandatory field",
12452     
12453      /**
12454      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12455      */
12456     minLength : 0,
12457     /**
12458      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12459      */
12460     maxLength : Number.MAX_VALUE,
12461     /**
12462      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12463      */
12464     minLengthText : "The minimum length for this field is {0}",
12465     /**
12466      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12467      */
12468     maxLengthText : "The maximum length for this field is {0}",
12469   
12470     
12471     /**
12472      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12473      * If available, this function will be called only after the basic validators all return true, and will be passed the
12474      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12475      */
12476     validator : null,
12477     /**
12478      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12479      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12480      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12481      */
12482     regex : null,
12483     /**
12484      * @cfg {String} regexText -- Depricated - use Invalid Text
12485      */
12486     regexText : "",
12487     
12488     /**
12489      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12490      */
12491     invalidText : "",
12492     
12493     
12494     
12495     autocomplete: false,
12496     
12497     
12498     fieldLabel : '',
12499     inputType : 'text',
12500     
12501     name : false,
12502     placeholder: false,
12503     before : false,
12504     after : false,
12505     size : false,
12506     hasFocus : false,
12507     preventMark: false,
12508     isFormField : true,
12509     value : '',
12510     labelWidth : 2,
12511     labelAlign : false,
12512     readOnly : false,
12513     align : false,
12514     formatedValue : false,
12515     forceFeedback : false,
12516     
12517     indicatorpos : 'left',
12518     
12519     labellg : 0,
12520     labelmd : 0,
12521     labelsm : 0,
12522     labelxs : 0,
12523     
12524     capture : '',
12525     accept : '',
12526     
12527     parentLabelAlign : function()
12528     {
12529         var parent = this;
12530         while (parent.parent()) {
12531             parent = parent.parent();
12532             if (typeof(parent.labelAlign) !='undefined') {
12533                 return parent.labelAlign;
12534             }
12535         }
12536         return 'left';
12537         
12538     },
12539     
12540     getAutoCreate : function()
12541     {
12542         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12543         
12544         var id = Roo.id();
12545         
12546         var cfg = {};
12547         
12548         if(this.inputType != 'hidden'){
12549             cfg.cls = 'form-group' //input-group
12550         }
12551         
12552         var input =  {
12553             tag: 'input',
12554             id : id,
12555             type : this.inputType,
12556             value : this.value,
12557             cls : 'form-control',
12558             placeholder : this.placeholder || '',
12559             autocomplete : this.autocomplete || 'new-password'
12560         };
12561         if (this.inputType == 'file') {
12562             input.style = 'overflow:hidden'; // why not in CSS?
12563         }
12564         
12565         if(this.capture.length){
12566             input.capture = this.capture;
12567         }
12568         
12569         if(this.accept.length){
12570             input.accept = this.accept + "/*";
12571         }
12572         
12573         if(this.align){
12574             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12575         }
12576         
12577         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12578             input.maxLength = this.maxLength;
12579         }
12580         
12581         if (this.disabled) {
12582             input.disabled=true;
12583         }
12584         
12585         if (this.readOnly) {
12586             input.readonly=true;
12587         }
12588         
12589         if (this.name) {
12590             input.name = this.name;
12591         }
12592         
12593         if (this.size) {
12594             input.cls += ' input-' + this.size;
12595         }
12596         
12597         var settings=this;
12598         ['xs','sm','md','lg'].map(function(size){
12599             if (settings[size]) {
12600                 cfg.cls += ' col-' + size + '-' + settings[size];
12601             }
12602         });
12603         
12604         var inputblock = input;
12605         
12606         var feedback = {
12607             tag: 'span',
12608             cls: 'glyphicon form-control-feedback'
12609         };
12610             
12611         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12612             
12613             inputblock = {
12614                 cls : 'has-feedback',
12615                 cn :  [
12616                     input,
12617                     feedback
12618                 ] 
12619             };  
12620         }
12621         
12622         if (this.before || this.after) {
12623             
12624             inputblock = {
12625                 cls : 'input-group',
12626                 cn :  [] 
12627             };
12628             
12629             if (this.before && typeof(this.before) == 'string') {
12630                 
12631                 inputblock.cn.push({
12632                     tag :'span',
12633                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12634                     html : this.before
12635                 });
12636             }
12637             if (this.before && typeof(this.before) == 'object') {
12638                 this.before = Roo.factory(this.before);
12639                 
12640                 inputblock.cn.push({
12641                     tag :'span',
12642                     cls : 'roo-input-before input-group-prepend   input-group-' +
12643                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12644                 });
12645             }
12646             
12647             inputblock.cn.push(input);
12648             
12649             if (this.after && typeof(this.after) == 'string') {
12650                 inputblock.cn.push({
12651                     tag :'span',
12652                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12653                     html : this.after
12654                 });
12655             }
12656             if (this.after && typeof(this.after) == 'object') {
12657                 this.after = Roo.factory(this.after);
12658                 
12659                 inputblock.cn.push({
12660                     tag :'span',
12661                     cls : 'roo-input-after input-group-append  input-group-' +
12662                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12663                 });
12664             }
12665             
12666             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12667                 inputblock.cls += ' has-feedback';
12668                 inputblock.cn.push(feedback);
12669             }
12670         };
12671         var indicator = {
12672             tag : 'i',
12673             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12674             tooltip : 'This field is required'
12675         };
12676         if (this.allowBlank ) {
12677             indicator.style = this.allowBlank ? ' display:none' : '';
12678         }
12679         if (align ==='left' && this.fieldLabel.length) {
12680             
12681             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12682             
12683             cfg.cn = [
12684                 indicator,
12685                 {
12686                     tag: 'label',
12687                     'for' :  id,
12688                     cls : 'control-label col-form-label',
12689                     html : this.fieldLabel
12690
12691                 },
12692                 {
12693                     cls : "", 
12694                     cn: [
12695                         inputblock
12696                     ]
12697                 }
12698             ];
12699             
12700             var labelCfg = cfg.cn[1];
12701             var contentCfg = cfg.cn[2];
12702             
12703             if(this.indicatorpos == 'right'){
12704                 cfg.cn = [
12705                     {
12706                         tag: 'label',
12707                         'for' :  id,
12708                         cls : 'control-label col-form-label',
12709                         cn : [
12710                             {
12711                                 tag : 'span',
12712                                 html : this.fieldLabel
12713                             },
12714                             indicator
12715                         ]
12716                     },
12717                     {
12718                         cls : "",
12719                         cn: [
12720                             inputblock
12721                         ]
12722                     }
12723
12724                 ];
12725                 
12726                 labelCfg = cfg.cn[0];
12727                 contentCfg = cfg.cn[1];
12728             
12729             }
12730             
12731             if(this.labelWidth > 12){
12732                 labelCfg.style = "width: " + this.labelWidth + 'px';
12733             }
12734             
12735             if(this.labelWidth < 13 && this.labelmd == 0){
12736                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12737             }
12738             
12739             if(this.labellg > 0){
12740                 labelCfg.cls += ' col-lg-' + this.labellg;
12741                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12742             }
12743             
12744             if(this.labelmd > 0){
12745                 labelCfg.cls += ' col-md-' + this.labelmd;
12746                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12747             }
12748             
12749             if(this.labelsm > 0){
12750                 labelCfg.cls += ' col-sm-' + this.labelsm;
12751                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12752             }
12753             
12754             if(this.labelxs > 0){
12755                 labelCfg.cls += ' col-xs-' + this.labelxs;
12756                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12757             }
12758             
12759             
12760         } else if ( this.fieldLabel.length) {
12761                 
12762             
12763             
12764             cfg.cn = [
12765                 {
12766                     tag : 'i',
12767                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12768                     tooltip : 'This field is required',
12769                     style : this.allowBlank ? ' display:none' : '' 
12770                 },
12771                 {
12772                     tag: 'label',
12773                    //cls : 'input-group-addon',
12774                     html : this.fieldLabel
12775
12776                 },
12777
12778                inputblock
12779
12780            ];
12781            
12782            if(this.indicatorpos == 'right'){
12783        
12784                 cfg.cn = [
12785                     {
12786                         tag: 'label',
12787                        //cls : 'input-group-addon',
12788                         html : this.fieldLabel
12789
12790                     },
12791                     {
12792                         tag : 'i',
12793                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12794                         tooltip : 'This field is required',
12795                         style : this.allowBlank ? ' display:none' : '' 
12796                     },
12797
12798                    inputblock
12799
12800                ];
12801
12802             }
12803
12804         } else {
12805             
12806             cfg.cn = [
12807
12808                     inputblock
12809
12810             ];
12811                 
12812                 
12813         };
12814         
12815         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12816            cfg.cls += ' navbar-form';
12817         }
12818         
12819         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12820             // on BS4 we do this only if not form 
12821             cfg.cls += ' navbar-form';
12822             cfg.tag = 'li';
12823         }
12824         
12825         return cfg;
12826         
12827     },
12828     /**
12829      * return the real input element.
12830      */
12831     inputEl: function ()
12832     {
12833         return this.el.select('input.form-control',true).first();
12834     },
12835     
12836     tooltipEl : function()
12837     {
12838         return this.inputEl();
12839     },
12840     
12841     indicatorEl : function()
12842     {
12843         if (Roo.bootstrap.version == 4) {
12844             return false; // not enabled in v4 yet.
12845         }
12846         
12847         var indicator = this.el.select('i.roo-required-indicator',true).first();
12848         
12849         if(!indicator){
12850             return false;
12851         }
12852         
12853         return indicator;
12854         
12855     },
12856     
12857     setDisabled : function(v)
12858     {
12859         var i  = this.inputEl().dom;
12860         if (!v) {
12861             i.removeAttribute('disabled');
12862             return;
12863             
12864         }
12865         i.setAttribute('disabled','true');
12866     },
12867     initEvents : function()
12868     {
12869           
12870         this.inputEl().on("keydown" , this.fireKey,  this);
12871         this.inputEl().on("focus", this.onFocus,  this);
12872         this.inputEl().on("blur", this.onBlur,  this);
12873         
12874         this.inputEl().relayEvent('keyup', this);
12875         this.inputEl().relayEvent('paste', this);
12876         
12877         this.indicator = this.indicatorEl();
12878         
12879         if(this.indicator){
12880             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12881         }
12882  
12883         // reference to original value for reset
12884         this.originalValue = this.getValue();
12885         //Roo.form.TextField.superclass.initEvents.call(this);
12886         if(this.validationEvent == 'keyup'){
12887             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12888             this.inputEl().on('keyup', this.filterValidation, this);
12889         }
12890         else if(this.validationEvent !== false){
12891             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12892         }
12893         
12894         if(this.selectOnFocus){
12895             this.on("focus", this.preFocus, this);
12896             
12897         }
12898         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12899             this.inputEl().on("keypress", this.filterKeys, this);
12900         } else {
12901             this.inputEl().relayEvent('keypress', this);
12902         }
12903        /* if(this.grow){
12904             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12905             this.el.on("click", this.autoSize,  this);
12906         }
12907         */
12908         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12909             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12910         }
12911         
12912         if (typeof(this.before) == 'object') {
12913             this.before.render(this.el.select('.roo-input-before',true).first());
12914         }
12915         if (typeof(this.after) == 'object') {
12916             this.after.render(this.el.select('.roo-input-after',true).first());
12917         }
12918         
12919         this.inputEl().on('change', this.onChange, this);
12920         
12921     },
12922     filterValidation : function(e){
12923         if(!e.isNavKeyPress()){
12924             this.validationTask.delay(this.validationDelay);
12925         }
12926     },
12927      /**
12928      * Validates the field value
12929      * @return {Boolean} True if the value is valid, else false
12930      */
12931     validate : function(){
12932         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12933         if(this.disabled || this.validateValue(this.getRawValue())){
12934             this.markValid();
12935             return true;
12936         }
12937         
12938         this.markInvalid();
12939         return false;
12940     },
12941     
12942     
12943     /**
12944      * Validates a value according to the field's validation rules and marks the field as invalid
12945      * if the validation fails
12946      * @param {Mixed} value The value to validate
12947      * @return {Boolean} True if the value is valid, else false
12948      */
12949     validateValue : function(value)
12950     {
12951         if(this.getVisibilityEl().hasClass('hidden')){
12952             return true;
12953         }
12954         
12955         if(value.length < 1)  { // if it's blank
12956             if(this.allowBlank){
12957                 return true;
12958             }
12959             return false;
12960         }
12961         
12962         if(value.length < this.minLength){
12963             return false;
12964         }
12965         if(value.length > this.maxLength){
12966             return false;
12967         }
12968         if(this.vtype){
12969             var vt = Roo.form.VTypes;
12970             if(!vt[this.vtype](value, this)){
12971                 return false;
12972             }
12973         }
12974         if(typeof this.validator == "function"){
12975             var msg = this.validator(value);
12976             if (typeof(msg) == 'string') {
12977                 this.invalidText = msg;
12978             }
12979             if(msg !== true){
12980                 return false;
12981             }
12982         }
12983         
12984         if(this.regex && !this.regex.test(value)){
12985             return false;
12986         }
12987         
12988         return true;
12989     },
12990     
12991      // private
12992     fireKey : function(e){
12993         //Roo.log('field ' + e.getKey());
12994         if(e.isNavKeyPress()){
12995             this.fireEvent("specialkey", this, e);
12996         }
12997     },
12998     focus : function (selectText){
12999         if(this.rendered){
13000             this.inputEl().focus();
13001             if(selectText === true){
13002                 this.inputEl().dom.select();
13003             }
13004         }
13005         return this;
13006     } ,
13007     
13008     onFocus : function(){
13009         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13010            // this.el.addClass(this.focusClass);
13011         }
13012         if(!this.hasFocus){
13013             this.hasFocus = true;
13014             this.startValue = this.getValue();
13015             this.fireEvent("focus", this);
13016         }
13017     },
13018     
13019     beforeBlur : Roo.emptyFn,
13020
13021     
13022     // private
13023     onBlur : function(){
13024         this.beforeBlur();
13025         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13026             //this.el.removeClass(this.focusClass);
13027         }
13028         this.hasFocus = false;
13029         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13030             this.validate();
13031         }
13032         var v = this.getValue();
13033         if(String(v) !== String(this.startValue)){
13034             this.fireEvent('change', this, v, this.startValue);
13035         }
13036         this.fireEvent("blur", this);
13037     },
13038     
13039     onChange : function(e)
13040     {
13041         var v = this.getValue();
13042         if(String(v) !== String(this.startValue)){
13043             this.fireEvent('change', this, v, this.startValue);
13044         }
13045         
13046     },
13047     
13048     /**
13049      * Resets the current field value to the originally loaded value and clears any validation messages
13050      */
13051     reset : function(){
13052         this.setValue(this.originalValue);
13053         this.validate();
13054     },
13055      /**
13056      * Returns the name of the field
13057      * @return {Mixed} name The name field
13058      */
13059     getName: function(){
13060         return this.name;
13061     },
13062      /**
13063      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13064      * @return {Mixed} value The field value
13065      */
13066     getValue : function(){
13067         
13068         var v = this.inputEl().getValue();
13069         
13070         return v;
13071     },
13072     /**
13073      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13074      * @return {Mixed} value The field value
13075      */
13076     getRawValue : function(){
13077         var v = this.inputEl().getValue();
13078         
13079         return v;
13080     },
13081     
13082     /**
13083      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13084      * @param {Mixed} value The value to set
13085      */
13086     setRawValue : function(v){
13087         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13088     },
13089     
13090     selectText : function(start, end){
13091         var v = this.getRawValue();
13092         if(v.length > 0){
13093             start = start === undefined ? 0 : start;
13094             end = end === undefined ? v.length : end;
13095             var d = this.inputEl().dom;
13096             if(d.setSelectionRange){
13097                 d.setSelectionRange(start, end);
13098             }else if(d.createTextRange){
13099                 var range = d.createTextRange();
13100                 range.moveStart("character", start);
13101                 range.moveEnd("character", v.length-end);
13102                 range.select();
13103             }
13104         }
13105     },
13106     
13107     /**
13108      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13109      * @param {Mixed} value The value to set
13110      */
13111     setValue : function(v){
13112         this.value = v;
13113         if(this.rendered){
13114             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13115             this.validate();
13116         }
13117     },
13118     
13119     /*
13120     processValue : function(value){
13121         if(this.stripCharsRe){
13122             var newValue = value.replace(this.stripCharsRe, '');
13123             if(newValue !== value){
13124                 this.setRawValue(newValue);
13125                 return newValue;
13126             }
13127         }
13128         return value;
13129     },
13130   */
13131     preFocus : function(){
13132         
13133         if(this.selectOnFocus){
13134             this.inputEl().dom.select();
13135         }
13136     },
13137     filterKeys : function(e){
13138         var k = e.getKey();
13139         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13140             return;
13141         }
13142         var c = e.getCharCode(), cc = String.fromCharCode(c);
13143         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13144             return;
13145         }
13146         if(!this.maskRe.test(cc)){
13147             e.stopEvent();
13148         }
13149     },
13150      /**
13151      * Clear any invalid styles/messages for this field
13152      */
13153     clearInvalid : function(){
13154         
13155         if(!this.el || this.preventMark){ // not rendered
13156             return;
13157         }
13158         
13159         
13160         this.el.removeClass([this.invalidClass, 'is-invalid']);
13161         
13162         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13163             
13164             var feedback = this.el.select('.form-control-feedback', true).first();
13165             
13166             if(feedback){
13167                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13168             }
13169             
13170         }
13171         
13172         if(this.indicator){
13173             this.indicator.removeClass('visible');
13174             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13175         }
13176         
13177         this.fireEvent('valid', this);
13178     },
13179     
13180      /**
13181      * Mark this field as valid
13182      */
13183     markValid : function()
13184     {
13185         if(!this.el  || this.preventMark){ // not rendered...
13186             return;
13187         }
13188         
13189         this.el.removeClass([this.invalidClass, this.validClass]);
13190         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13191
13192         var feedback = this.el.select('.form-control-feedback', true).first();
13193             
13194         if(feedback){
13195             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13196         }
13197         
13198         if(this.indicator){
13199             this.indicator.removeClass('visible');
13200             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13201         }
13202         
13203         if(this.disabled){
13204             return;
13205         }
13206         
13207            
13208         if(this.allowBlank && !this.getRawValue().length){
13209             return;
13210         }
13211         if (Roo.bootstrap.version == 3) {
13212             this.el.addClass(this.validClass);
13213         } else {
13214             this.inputEl().addClass('is-valid');
13215         }
13216
13217         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13218             
13219             var feedback = this.el.select('.form-control-feedback', true).first();
13220             
13221             if(feedback){
13222                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13223                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13224             }
13225             
13226         }
13227         
13228         this.fireEvent('valid', this);
13229     },
13230     
13231      /**
13232      * Mark this field as invalid
13233      * @param {String} msg The validation message
13234      */
13235     markInvalid : function(msg)
13236     {
13237         if(!this.el  || this.preventMark){ // not rendered
13238             return;
13239         }
13240         
13241         this.el.removeClass([this.invalidClass, this.validClass]);
13242         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13243         
13244         var feedback = this.el.select('.form-control-feedback', true).first();
13245             
13246         if(feedback){
13247             this.el.select('.form-control-feedback', true).first().removeClass(
13248                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13249         }
13250
13251         if(this.disabled){
13252             return;
13253         }
13254         
13255         if(this.allowBlank && !this.getRawValue().length){
13256             return;
13257         }
13258         
13259         if(this.indicator){
13260             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13261             this.indicator.addClass('visible');
13262         }
13263         if (Roo.bootstrap.version == 3) {
13264             this.el.addClass(this.invalidClass);
13265         } else {
13266             this.inputEl().addClass('is-invalid');
13267         }
13268         
13269         
13270         
13271         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13272             
13273             var feedback = this.el.select('.form-control-feedback', true).first();
13274             
13275             if(feedback){
13276                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13277                 
13278                 if(this.getValue().length || this.forceFeedback){
13279                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13280                 }
13281                 
13282             }
13283             
13284         }
13285         
13286         this.fireEvent('invalid', this, msg);
13287     },
13288     // private
13289     SafariOnKeyDown : function(event)
13290     {
13291         // this is a workaround for a password hang bug on chrome/ webkit.
13292         if (this.inputEl().dom.type != 'password') {
13293             return;
13294         }
13295         
13296         var isSelectAll = false;
13297         
13298         if(this.inputEl().dom.selectionEnd > 0){
13299             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13300         }
13301         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13302             event.preventDefault();
13303             this.setValue('');
13304             return;
13305         }
13306         
13307         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13308             
13309             event.preventDefault();
13310             // this is very hacky as keydown always get's upper case.
13311             //
13312             var cc = String.fromCharCode(event.getCharCode());
13313             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13314             
13315         }
13316     },
13317     adjustWidth : function(tag, w){
13318         tag = tag.toLowerCase();
13319         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13320             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13321                 if(tag == 'input'){
13322                     return w + 2;
13323                 }
13324                 if(tag == 'textarea'){
13325                     return w-2;
13326                 }
13327             }else if(Roo.isOpera){
13328                 if(tag == 'input'){
13329                     return w + 2;
13330                 }
13331                 if(tag == 'textarea'){
13332                     return w-2;
13333                 }
13334             }
13335         }
13336         return w;
13337     },
13338     
13339     setFieldLabel : function(v)
13340     {
13341         if(!this.rendered){
13342             return;
13343         }
13344         
13345         if(this.indicatorEl()){
13346             var ar = this.el.select('label > span',true);
13347             
13348             if (ar.elements.length) {
13349                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13350                 this.fieldLabel = v;
13351                 return;
13352             }
13353             
13354             var br = this.el.select('label',true);
13355             
13356             if(br.elements.length) {
13357                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13358                 this.fieldLabel = v;
13359                 return;
13360             }
13361             
13362             Roo.log('Cannot Found any of label > span || label in input');
13363             return;
13364         }
13365         
13366         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13367         this.fieldLabel = v;
13368         
13369         
13370     }
13371 });
13372
13373  
13374 /*
13375  * - LGPL
13376  *
13377  * Input
13378  * 
13379  */
13380
13381 /**
13382  * @class Roo.bootstrap.form.TextArea
13383  * @extends Roo.bootstrap.form.Input
13384  * Bootstrap TextArea class
13385  * @cfg {Number} cols Specifies the visible width of a text area
13386  * @cfg {Number} rows Specifies the visible number of lines in a text area
13387  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13388  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13389  * @cfg {string} html text
13390  * 
13391  * @constructor
13392  * Create a new TextArea
13393  * @param {Object} config The config object
13394  */
13395
13396 Roo.bootstrap.form.TextArea = function(config){
13397     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13398    
13399 };
13400
13401 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13402      
13403     cols : false,
13404     rows : 5,
13405     readOnly : false,
13406     warp : 'soft',
13407     resize : false,
13408     value: false,
13409     html: false,
13410     
13411     getAutoCreate : function(){
13412         
13413         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13414         
13415         var id = Roo.id();
13416         
13417         var cfg = {};
13418         
13419         if(this.inputType != 'hidden'){
13420             cfg.cls = 'form-group' //input-group
13421         }
13422         
13423         var input =  {
13424             tag: 'textarea',
13425             id : id,
13426             warp : this.warp,
13427             rows : this.rows,
13428             value : this.value || '',
13429             html: this.html || '',
13430             cls : 'form-control',
13431             placeholder : this.placeholder || '' 
13432             
13433         };
13434         
13435         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13436             input.maxLength = this.maxLength;
13437         }
13438         
13439         if(this.resize){
13440             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13441         }
13442         
13443         if(this.cols){
13444             input.cols = this.cols;
13445         }
13446         
13447         if (this.readOnly) {
13448             input.readonly = true;
13449         }
13450         
13451         if (this.name) {
13452             input.name = this.name;
13453         }
13454         
13455         if (this.size) {
13456             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13457         }
13458         
13459         var settings=this;
13460         ['xs','sm','md','lg'].map(function(size){
13461             if (settings[size]) {
13462                 cfg.cls += ' col-' + size + '-' + settings[size];
13463             }
13464         });
13465         
13466         var inputblock = input;
13467         
13468         if(this.hasFeedback && !this.allowBlank){
13469             
13470             var feedback = {
13471                 tag: 'span',
13472                 cls: 'glyphicon form-control-feedback'
13473             };
13474
13475             inputblock = {
13476                 cls : 'has-feedback',
13477                 cn :  [
13478                     input,
13479                     feedback
13480                 ] 
13481             };  
13482         }
13483         
13484         
13485         if (this.before || this.after) {
13486             
13487             inputblock = {
13488                 cls : 'input-group',
13489                 cn :  [] 
13490             };
13491             if (this.before) {
13492                 inputblock.cn.push({
13493                     tag :'span',
13494                     cls : 'input-group-addon',
13495                     html : this.before
13496                 });
13497             }
13498             
13499             inputblock.cn.push(input);
13500             
13501             if(this.hasFeedback && !this.allowBlank){
13502                 inputblock.cls += ' has-feedback';
13503                 inputblock.cn.push(feedback);
13504             }
13505             
13506             if (this.after) {
13507                 inputblock.cn.push({
13508                     tag :'span',
13509                     cls : 'input-group-addon',
13510                     html : this.after
13511                 });
13512             }
13513             
13514         }
13515         
13516         if (align ==='left' && this.fieldLabel.length) {
13517             cfg.cn = [
13518                 {
13519                     tag: 'label',
13520                     'for' :  id,
13521                     cls : 'control-label',
13522                     html : this.fieldLabel
13523                 },
13524                 {
13525                     cls : "",
13526                     cn: [
13527                         inputblock
13528                     ]
13529                 }
13530
13531             ];
13532             
13533             if(this.labelWidth > 12){
13534                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13535             }
13536
13537             if(this.labelWidth < 13 && this.labelmd == 0){
13538                 this.labelmd = this.labelWidth;
13539             }
13540
13541             if(this.labellg > 0){
13542                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13543                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13544             }
13545
13546             if(this.labelmd > 0){
13547                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13548                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13549             }
13550
13551             if(this.labelsm > 0){
13552                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13553                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13554             }
13555
13556             if(this.labelxs > 0){
13557                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13558                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13559             }
13560             
13561         } else if ( this.fieldLabel.length) {
13562             cfg.cn = [
13563
13564                {
13565                    tag: 'label',
13566                    //cls : 'input-group-addon',
13567                    html : this.fieldLabel
13568
13569                },
13570
13571                inputblock
13572
13573            ];
13574
13575         } else {
13576
13577             cfg.cn = [
13578
13579                 inputblock
13580
13581             ];
13582                 
13583         }
13584         
13585         if (this.disabled) {
13586             input.disabled=true;
13587         }
13588         
13589         return cfg;
13590         
13591     },
13592     /**
13593      * return the real textarea element.
13594      */
13595     inputEl: function ()
13596     {
13597         return this.el.select('textarea.form-control',true).first();
13598     },
13599     
13600     /**
13601      * Clear any invalid styles/messages for this field
13602      */
13603     clearInvalid : function()
13604     {
13605         
13606         if(!this.el || this.preventMark){ // not rendered
13607             return;
13608         }
13609         
13610         var label = this.el.select('label', true).first();
13611         var icon = this.el.select('i.fa-star', true).first();
13612         
13613         if(label && icon){
13614             icon.remove();
13615         }
13616         this.el.removeClass( this.validClass);
13617         this.inputEl().removeClass('is-invalid');
13618          
13619         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13620             
13621             var feedback = this.el.select('.form-control-feedback', true).first();
13622             
13623             if(feedback){
13624                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13625             }
13626             
13627         }
13628         
13629         this.fireEvent('valid', this);
13630     },
13631     
13632      /**
13633      * Mark this field as valid
13634      */
13635     markValid : function()
13636     {
13637         if(!this.el  || this.preventMark){ // not rendered
13638             return;
13639         }
13640         
13641         this.el.removeClass([this.invalidClass, this.validClass]);
13642         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13643         
13644         var feedback = this.el.select('.form-control-feedback', true).first();
13645             
13646         if(feedback){
13647             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13648         }
13649
13650         if(this.disabled || this.allowBlank){
13651             return;
13652         }
13653         
13654         var label = this.el.select('label', true).first();
13655         var icon = this.el.select('i.fa-star', true).first();
13656         
13657         if(label && icon){
13658             icon.remove();
13659         }
13660         if (Roo.bootstrap.version == 3) {
13661             this.el.addClass(this.validClass);
13662         } else {
13663             this.inputEl().addClass('is-valid');
13664         }
13665         
13666         
13667         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13668             
13669             var feedback = this.el.select('.form-control-feedback', true).first();
13670             
13671             if(feedback){
13672                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13673                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13674             }
13675             
13676         }
13677         
13678         this.fireEvent('valid', this);
13679     },
13680     
13681      /**
13682      * Mark this field as invalid
13683      * @param {String} msg The validation message
13684      */
13685     markInvalid : function(msg)
13686     {
13687         if(!this.el  || this.preventMark){ // not rendered
13688             return;
13689         }
13690         
13691         this.el.removeClass([this.invalidClass, this.validClass]);
13692         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13693         
13694         var feedback = this.el.select('.form-control-feedback', true).first();
13695             
13696         if(feedback){
13697             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13698         }
13699
13700         if(this.disabled || this.allowBlank){
13701             return;
13702         }
13703         
13704         var label = this.el.select('label', true).first();
13705         var icon = this.el.select('i.fa-star', true).first();
13706         
13707         if(!this.getValue().length && label && !icon){
13708             this.el.createChild({
13709                 tag : 'i',
13710                 cls : 'text-danger fa fa-lg fa-star',
13711                 tooltip : 'This field is required',
13712                 style : 'margin-right:5px;'
13713             }, label, true);
13714         }
13715         
13716         if (Roo.bootstrap.version == 3) {
13717             this.el.addClass(this.invalidClass);
13718         } else {
13719             this.inputEl().addClass('is-invalid');
13720         }
13721         
13722         // fixme ... this may be depricated need to test..
13723         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13724             
13725             var feedback = this.el.select('.form-control-feedback', true).first();
13726             
13727             if(feedback){
13728                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13729                 
13730                 if(this.getValue().length || this.forceFeedback){
13731                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13732                 }
13733                 
13734             }
13735             
13736         }
13737         
13738         this.fireEvent('invalid', this, msg);
13739     }
13740 });
13741
13742  
13743 /*
13744  * - LGPL
13745  *
13746  * trigger field - base class for combo..
13747  * 
13748  */
13749  
13750 /**
13751  * @class Roo.bootstrap.form.TriggerField
13752  * @extends Roo.bootstrap.form.Input
13753  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13754  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13755  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13756  * for which you can provide a custom implementation.  For example:
13757  * <pre><code>
13758 var trigger = new Roo.bootstrap.form.TriggerField();
13759 trigger.onTriggerClick = myTriggerFn;
13760 trigger.applyTo('my-field');
13761 </code></pre>
13762  *
13763  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13764  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13765  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13766  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13767  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13768
13769  * @constructor
13770  * Create a new TriggerField.
13771  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13772  * to the base TextField)
13773  */
13774 Roo.bootstrap.form.TriggerField = function(config){
13775     this.mimicing = false;
13776     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13777 };
13778
13779 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13780     /**
13781      * @cfg {String} triggerClass A CSS class to apply to the trigger
13782      */
13783      /**
13784      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13785      */
13786     hideTrigger:false,
13787
13788     /**
13789      * @cfg {Boolean} removable (true|false) special filter default false
13790      */
13791     removable : false,
13792     
13793     /** @cfg {Boolean} grow @hide */
13794     /** @cfg {Number} growMin @hide */
13795     /** @cfg {Number} growMax @hide */
13796
13797     /**
13798      * @hide 
13799      * @method
13800      */
13801     autoSize: Roo.emptyFn,
13802     // private
13803     monitorTab : true,
13804     // private
13805     deferHeight : true,
13806
13807     
13808     actionMode : 'wrap',
13809     
13810     caret : false,
13811     
13812     
13813     getAutoCreate : function(){
13814        
13815         var align = this.labelAlign || this.parentLabelAlign();
13816         
13817         var id = Roo.id();
13818         
13819         var cfg = {
13820             cls: 'form-group' //input-group
13821         };
13822         
13823         
13824         var input =  {
13825             tag: 'input',
13826             id : id,
13827             type : this.inputType,
13828             cls : 'form-control',
13829             autocomplete: 'new-password',
13830             placeholder : this.placeholder || '' 
13831             
13832         };
13833         if (this.name) {
13834             input.name = this.name;
13835         }
13836         if (this.size) {
13837             input.cls += ' input-' + this.size;
13838         }
13839         
13840         if (this.disabled) {
13841             input.disabled=true;
13842         }
13843         
13844         var inputblock = input;
13845         
13846         if(this.hasFeedback && !this.allowBlank){
13847             
13848             var feedback = {
13849                 tag: 'span',
13850                 cls: 'glyphicon form-control-feedback'
13851             };
13852             
13853             if(this.removable && !this.editable  ){
13854                 inputblock = {
13855                     cls : 'has-feedback',
13856                     cn :  [
13857                         inputblock,
13858                         {
13859                             tag: 'button',
13860                             html : 'x',
13861                             cls : 'roo-combo-removable-btn close'
13862                         },
13863                         feedback
13864                     ] 
13865                 };
13866             } else {
13867                 inputblock = {
13868                     cls : 'has-feedback',
13869                     cn :  [
13870                         inputblock,
13871                         feedback
13872                     ] 
13873                 };
13874             }
13875
13876         } else {
13877             if(this.removable && !this.editable ){
13878                 inputblock = {
13879                     cls : 'roo-removable',
13880                     cn :  [
13881                         inputblock,
13882                         {
13883                             tag: 'button',
13884                             html : 'x',
13885                             cls : 'roo-combo-removable-btn close'
13886                         }
13887                     ] 
13888                 };
13889             }
13890         }
13891         
13892         if (this.before || this.after) {
13893             
13894             inputblock = {
13895                 cls : 'input-group',
13896                 cn :  [] 
13897             };
13898             if (this.before) {
13899                 inputblock.cn.push({
13900                     tag :'span',
13901                     cls : 'input-group-addon input-group-prepend input-group-text',
13902                     html : this.before
13903                 });
13904             }
13905             
13906             inputblock.cn.push(input);
13907             
13908             if(this.hasFeedback && !this.allowBlank){
13909                 inputblock.cls += ' has-feedback';
13910                 inputblock.cn.push(feedback);
13911             }
13912             
13913             if (this.after) {
13914                 inputblock.cn.push({
13915                     tag :'span',
13916                     cls : 'input-group-addon input-group-append input-group-text',
13917                     html : this.after
13918                 });
13919             }
13920             
13921         };
13922         
13923       
13924         
13925         var ibwrap = inputblock;
13926         
13927         if(this.multiple){
13928             ibwrap = {
13929                 tag: 'ul',
13930                 cls: 'roo-select2-choices',
13931                 cn:[
13932                     {
13933                         tag: 'li',
13934                         cls: 'roo-select2-search-field',
13935                         cn: [
13936
13937                             inputblock
13938                         ]
13939                     }
13940                 ]
13941             };
13942                 
13943         }
13944         
13945         var combobox = {
13946             cls: 'roo-select2-container input-group',
13947             cn: [
13948                  {
13949                     tag: 'input',
13950                     type : 'hidden',
13951                     cls: 'form-hidden-field'
13952                 },
13953                 ibwrap
13954             ]
13955         };
13956         
13957         if(!this.multiple && this.showToggleBtn){
13958             
13959             var caret = {
13960                         tag: 'span',
13961                         cls: 'caret'
13962              };
13963             if (this.caret != false) {
13964                 caret = {
13965                      tag: 'i',
13966                      cls: 'fa fa-' + this.caret
13967                 };
13968                 
13969             }
13970             
13971             combobox.cn.push({
13972                 tag :'span',
13973                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13974                 cn : [
13975                     Roo.bootstrap.version == 3 ? caret : '',
13976                     {
13977                         tag: 'span',
13978                         cls: 'combobox-clear',
13979                         cn  : [
13980                             {
13981                                 tag : 'i',
13982                                 cls: 'icon-remove'
13983                             }
13984                         ]
13985                     }
13986                 ]
13987
13988             })
13989         }
13990         
13991         if(this.multiple){
13992             combobox.cls += ' roo-select2-container-multi';
13993         }
13994          var indicator = {
13995             tag : 'i',
13996             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13997             tooltip : 'This field is required'
13998         };
13999         if (Roo.bootstrap.version == 4) {
14000             indicator = {
14001                 tag : 'i',
14002                 style : 'display:none'
14003             };
14004         }
14005         
14006         
14007         if (align ==='left' && this.fieldLabel.length) {
14008             
14009             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14010
14011             cfg.cn = [
14012                 indicator,
14013                 {
14014                     tag: 'label',
14015                     'for' :  id,
14016                     cls : 'control-label',
14017                     html : this.fieldLabel
14018
14019                 },
14020                 {
14021                     cls : "", 
14022                     cn: [
14023                         combobox
14024                     ]
14025                 }
14026
14027             ];
14028             
14029             var labelCfg = cfg.cn[1];
14030             var contentCfg = cfg.cn[2];
14031             
14032             if(this.indicatorpos == 'right'){
14033                 cfg.cn = [
14034                     {
14035                         tag: 'label',
14036                         'for' :  id,
14037                         cls : 'control-label',
14038                         cn : [
14039                             {
14040                                 tag : 'span',
14041                                 html : this.fieldLabel
14042                             },
14043                             indicator
14044                         ]
14045                     },
14046                     {
14047                         cls : "", 
14048                         cn: [
14049                             combobox
14050                         ]
14051                     }
14052
14053                 ];
14054                 
14055                 labelCfg = cfg.cn[0];
14056                 contentCfg = cfg.cn[1];
14057             }
14058             
14059             if(this.labelWidth > 12){
14060                 labelCfg.style = "width: " + this.labelWidth + 'px';
14061             }
14062             
14063             if(this.labelWidth < 13 && this.labelmd == 0){
14064                 this.labelmd = this.labelWidth;
14065             }
14066             
14067             if(this.labellg > 0){
14068                 labelCfg.cls += ' col-lg-' + this.labellg;
14069                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14070             }
14071             
14072             if(this.labelmd > 0){
14073                 labelCfg.cls += ' col-md-' + this.labelmd;
14074                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14075             }
14076             
14077             if(this.labelsm > 0){
14078                 labelCfg.cls += ' col-sm-' + this.labelsm;
14079                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14080             }
14081             
14082             if(this.labelxs > 0){
14083                 labelCfg.cls += ' col-xs-' + this.labelxs;
14084                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14085             }
14086             
14087         } else if ( this.fieldLabel.length) {
14088 //                Roo.log(" label");
14089             cfg.cn = [
14090                 indicator,
14091                {
14092                    tag: 'label',
14093                    //cls : 'input-group-addon',
14094                    html : this.fieldLabel
14095
14096                },
14097
14098                combobox
14099
14100             ];
14101             
14102             if(this.indicatorpos == 'right'){
14103                 
14104                 cfg.cn = [
14105                     {
14106                        tag: 'label',
14107                        cn : [
14108                            {
14109                                tag : 'span',
14110                                html : this.fieldLabel
14111                            },
14112                            indicator
14113                        ]
14114
14115                     },
14116                     combobox
14117
14118                 ];
14119
14120             }
14121
14122         } else {
14123             
14124 //                Roo.log(" no label && no align");
14125                 cfg = combobox
14126                      
14127                 
14128         }
14129         
14130         var settings=this;
14131         ['xs','sm','md','lg'].map(function(size){
14132             if (settings[size]) {
14133                 cfg.cls += ' col-' + size + '-' + settings[size];
14134             }
14135         });
14136         
14137         return cfg;
14138         
14139     },
14140     
14141     
14142     
14143     // private
14144     onResize : function(w, h){
14145 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14146 //        if(typeof w == 'number'){
14147 //            var x = w - this.trigger.getWidth();
14148 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14149 //            this.trigger.setStyle('left', x+'px');
14150 //        }
14151     },
14152
14153     // private
14154     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14155
14156     // private
14157     getResizeEl : function(){
14158         return this.inputEl();
14159     },
14160
14161     // private
14162     getPositionEl : function(){
14163         return this.inputEl();
14164     },
14165
14166     // private
14167     alignErrorIcon : function(){
14168         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14169     },
14170
14171     // private
14172     initEvents : function(){
14173         
14174         this.createList();
14175         
14176         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14177         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14178         if(!this.multiple && this.showToggleBtn){
14179             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14180             if(this.hideTrigger){
14181                 this.trigger.setDisplayed(false);
14182             }
14183             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14184         }
14185         
14186         if(this.multiple){
14187             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14188         }
14189         
14190         if(this.removable && !this.editable && !this.tickable){
14191             var close = this.closeTriggerEl();
14192             
14193             if(close){
14194                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14195                 close.on('click', this.removeBtnClick, this, close);
14196             }
14197         }
14198         
14199         //this.trigger.addClassOnOver('x-form-trigger-over');
14200         //this.trigger.addClassOnClick('x-form-trigger-click');
14201         
14202         //if(!this.width){
14203         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14204         //}
14205     },
14206     
14207     closeTriggerEl : function()
14208     {
14209         var close = this.el.select('.roo-combo-removable-btn', true).first();
14210         return close ? close : false;
14211     },
14212     
14213     removeBtnClick : function(e, h, el)
14214     {
14215         e.preventDefault();
14216         
14217         if(this.fireEvent("remove", this) !== false){
14218             this.reset();
14219             this.fireEvent("afterremove", this)
14220         }
14221     },
14222     
14223     createList : function()
14224     {
14225         this.list = Roo.get(document.body).createChild({
14226             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14227             cls: 'typeahead typeahead-long dropdown-menu shadow',
14228             style: 'display:none'
14229         });
14230         
14231         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14232         
14233     },
14234
14235     // private
14236     initTrigger : function(){
14237        
14238     },
14239
14240     // private
14241     onDestroy : function(){
14242         if(this.trigger){
14243             this.trigger.removeAllListeners();
14244           //  this.trigger.remove();
14245         }
14246         //if(this.wrap){
14247         //    this.wrap.remove();
14248         //}
14249         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14250     },
14251
14252     // private
14253     onFocus : function(){
14254         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14255         /*
14256         if(!this.mimicing){
14257             this.wrap.addClass('x-trigger-wrap-focus');
14258             this.mimicing = true;
14259             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14260             if(this.monitorTab){
14261                 this.el.on("keydown", this.checkTab, this);
14262             }
14263         }
14264         */
14265     },
14266
14267     // private
14268     checkTab : function(e){
14269         if(e.getKey() == e.TAB){
14270             this.triggerBlur();
14271         }
14272     },
14273
14274     // private
14275     onBlur : function(){
14276         // do nothing
14277     },
14278
14279     // private
14280     mimicBlur : function(e, t){
14281         /*
14282         if(!this.wrap.contains(t) && this.validateBlur()){
14283             this.triggerBlur();
14284         }
14285         */
14286     },
14287
14288     // private
14289     triggerBlur : function(){
14290         this.mimicing = false;
14291         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14292         if(this.monitorTab){
14293             this.el.un("keydown", this.checkTab, this);
14294         }
14295         //this.wrap.removeClass('x-trigger-wrap-focus');
14296         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14297     },
14298
14299     // private
14300     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14301     validateBlur : function(e, t){
14302         return true;
14303     },
14304
14305     // private
14306     onDisable : function(){
14307         this.inputEl().dom.disabled = true;
14308         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14309         //if(this.wrap){
14310         //    this.wrap.addClass('x-item-disabled');
14311         //}
14312     },
14313
14314     // private
14315     onEnable : function(){
14316         this.inputEl().dom.disabled = false;
14317         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14318         //if(this.wrap){
14319         //    this.el.removeClass('x-item-disabled');
14320         //}
14321     },
14322
14323     // private
14324     onShow : function(){
14325         var ae = this.getActionEl();
14326         
14327         if(ae){
14328             ae.dom.style.display = '';
14329             ae.dom.style.visibility = 'visible';
14330         }
14331     },
14332
14333     // private
14334     
14335     onHide : function(){
14336         var ae = this.getActionEl();
14337         ae.dom.style.display = 'none';
14338     },
14339
14340     /**
14341      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14342      * by an implementing function.
14343      * @method
14344      * @param {EventObject} e
14345      */
14346     onTriggerClick : Roo.emptyFn
14347 });
14348  
14349 /*
14350 * Licence: LGPL
14351 */
14352
14353 /**
14354  * @class Roo.bootstrap.form.CardUploader
14355  * @extends Roo.bootstrap.Button
14356  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14357  * @cfg {Number} errorTimeout default 3000
14358  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14359  * @cfg {Array}  html The button text.
14360
14361  *
14362  * @constructor
14363  * Create a new CardUploader
14364  * @param {Object} config The config object
14365  */
14366
14367 Roo.bootstrap.form.CardUploader = function(config){
14368     
14369  
14370     
14371     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14372     
14373     
14374     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14375         return r.data.id
14376      });
14377     
14378      this.addEvents({
14379          // raw events
14380         /**
14381          * @event preview
14382          * When a image is clicked on - and needs to display a slideshow or similar..
14383          * @param {Roo.bootstrap.Card} this
14384          * @param {Object} The image information data 
14385          *
14386          */
14387         'preview' : true,
14388          /**
14389          * @event download
14390          * When a the download link is clicked
14391          * @param {Roo.bootstrap.Card} this
14392          * @param {Object} The image information data  contains 
14393          */
14394         'download' : true
14395         
14396     });
14397 };
14398  
14399 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14400     
14401      
14402     errorTimeout : 3000,
14403      
14404     images : false,
14405    
14406     fileCollection : false,
14407     allowBlank : true,
14408     
14409     getAutoCreate : function()
14410     {
14411         
14412         var cfg =  {
14413             cls :'form-group' ,
14414             cn : [
14415                
14416                 {
14417                     tag: 'label',
14418                    //cls : 'input-group-addon',
14419                     html : this.fieldLabel
14420
14421                 },
14422
14423                 {
14424                     tag: 'input',
14425                     type : 'hidden',
14426                     name : this.name,
14427                     value : this.value,
14428                     cls : 'd-none  form-control'
14429                 },
14430                 
14431                 {
14432                     tag: 'input',
14433                     multiple : 'multiple',
14434                     type : 'file',
14435                     cls : 'd-none  roo-card-upload-selector'
14436                 },
14437                 
14438                 {
14439                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14440                 },
14441                 {
14442                     cls : 'card-columns roo-card-uploader-container'
14443                 }
14444
14445             ]
14446         };
14447            
14448          
14449         return cfg;
14450     },
14451     
14452     getChildContainer : function() /// what children are added to.
14453     {
14454         return this.containerEl;
14455     },
14456    
14457     getButtonContainer : function() /// what children are added to.
14458     {
14459         return this.el.select(".roo-card-uploader-button-container").first();
14460     },
14461    
14462     initEvents : function()
14463     {
14464         
14465         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14466         
14467         var t = this;
14468         this.addxtype({
14469             xns: Roo.bootstrap,
14470
14471             xtype : 'Button',
14472             container_method : 'getButtonContainer' ,            
14473             html :  this.html, // fix changable?
14474             cls : 'w-100 ',
14475             listeners : {
14476                 'click' : function(btn, e) {
14477                     t.onClick(e);
14478                 }
14479             }
14480         });
14481         
14482         
14483         
14484         
14485         this.urlAPI = (window.createObjectURL && window) || 
14486                                 (window.URL && URL.revokeObjectURL && URL) || 
14487                                 (window.webkitURL && webkitURL);
14488                         
14489          
14490          
14491          
14492         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14493         
14494         this.selectorEl.on('change', this.onFileSelected, this);
14495         if (this.images) {
14496             var t = this;
14497             this.images.forEach(function(img) {
14498                 t.addCard(img)
14499             });
14500             this.images = false;
14501         }
14502         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14503          
14504        
14505     },
14506     
14507    
14508     onClick : function(e)
14509     {
14510         e.preventDefault();
14511          
14512         this.selectorEl.dom.click();
14513          
14514     },
14515     
14516     onFileSelected : function(e)
14517     {
14518         e.preventDefault();
14519         
14520         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14521             return;
14522         }
14523         
14524         Roo.each(this.selectorEl.dom.files, function(file){    
14525             this.addFile(file);
14526         }, this);
14527          
14528     },
14529     
14530       
14531     
14532       
14533     
14534     addFile : function(file)
14535     {
14536            
14537         if(typeof(file) === 'string'){
14538             throw "Add file by name?"; // should not happen
14539             return;
14540         }
14541         
14542         if(!file || !this.urlAPI){
14543             return;
14544         }
14545         
14546         // file;
14547         // file.type;
14548         
14549         var _this = this;
14550         
14551         
14552         var url = _this.urlAPI.createObjectURL( file);
14553            
14554         this.addCard({
14555             id : Roo.bootstrap.form.CardUploader.ID--,
14556             is_uploaded : false,
14557             src : url,
14558             srcfile : file,
14559             title : file.name,
14560             mimetype : file.type,
14561             preview : false,
14562             is_deleted : 0
14563         });
14564         
14565     },
14566     
14567     /**
14568      * addCard - add an Attachment to the uploader
14569      * @param data - the data about the image to upload
14570      *
14571      * {
14572           id : 123
14573           title : "Title of file",
14574           is_uploaded : false,
14575           src : "http://.....",
14576           srcfile : { the File upload object },
14577           mimetype : file.type,
14578           preview : false,
14579           is_deleted : 0
14580           .. any other data...
14581         }
14582      *
14583      * 
14584     */
14585     
14586     addCard : function (data)
14587     {
14588         // hidden input element?
14589         // if the file is not an image...
14590         //then we need to use something other that and header_image
14591         var t = this;
14592         //   remove.....
14593         var footer = [
14594             {
14595                 xns : Roo.bootstrap,
14596                 xtype : 'CardFooter',
14597                  items: [
14598                     {
14599                         xns : Roo.bootstrap,
14600                         xtype : 'Element',
14601                         cls : 'd-flex',
14602                         items : [
14603                             
14604                             {
14605                                 xns : Roo.bootstrap,
14606                                 xtype : 'Button',
14607                                 html : String.format("<small>{0}</small>", data.title),
14608                                 cls : 'col-10 text-left',
14609                                 size: 'sm',
14610                                 weight: 'link',
14611                                 fa : 'download',
14612                                 listeners : {
14613                                     click : function() {
14614                                      
14615                                         t.fireEvent( "download", t, data );
14616                                     }
14617                                 }
14618                             },
14619                           
14620                             {
14621                                 xns : Roo.bootstrap,
14622                                 xtype : 'Button',
14623                                 style: 'max-height: 28px; ',
14624                                 size : 'sm',
14625                                 weight: 'danger',
14626                                 cls : 'col-2',
14627                                 fa : 'times',
14628                                 listeners : {
14629                                     click : function() {
14630                                         t.removeCard(data.id)
14631                                     }
14632                                 }
14633                             }
14634                         ]
14635                     }
14636                     
14637                 ] 
14638             }
14639             
14640         ];
14641         
14642         var cn = this.addxtype(
14643             {
14644                  
14645                 xns : Roo.bootstrap,
14646                 xtype : 'Card',
14647                 closeable : true,
14648                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14649                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14650                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14651                 data : data,
14652                 html : false,
14653                  
14654                 items : footer,
14655                 initEvents : function() {
14656                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14657                     var card = this;
14658                     this.imgEl = this.el.select('.card-img-top').first();
14659                     if (this.imgEl) {
14660                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14661                         this.imgEl.set({ 'pointer' : 'cursor' });
14662                                   
14663                     }
14664                     this.getCardFooter().addClass('p-1');
14665                     
14666                   
14667                 }
14668                 
14669             }
14670         );
14671         // dont' really need ot update items.
14672         // this.items.push(cn);
14673         this.fileCollection.add(cn);
14674         
14675         if (!data.srcfile) {
14676             this.updateInput();
14677             return;
14678         }
14679             
14680         var _t = this;
14681         var reader = new FileReader();
14682         reader.addEventListener("load", function() {  
14683             data.srcdata =  reader.result;
14684             _t.updateInput();
14685         });
14686         reader.readAsDataURL(data.srcfile);
14687         
14688         
14689         
14690     },
14691     removeCard : function(id)
14692     {
14693         
14694         var card  = this.fileCollection.get(id);
14695         card.data.is_deleted = 1;
14696         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14697         //this.fileCollection.remove(card);
14698         //this.items = this.items.filter(function(e) { return e != card });
14699         // dont' really need ot update items.
14700         card.el.dom.parentNode.removeChild(card.el.dom);
14701         this.updateInput();
14702
14703         
14704     },
14705     reset: function()
14706     {
14707         this.fileCollection.each(function(card) {
14708             if (card.el.dom && card.el.dom.parentNode) {
14709                 card.el.dom.parentNode.removeChild(card.el.dom);
14710             }
14711         });
14712         this.fileCollection.clear();
14713         this.updateInput();
14714     },
14715     
14716     updateInput : function()
14717     {
14718          var data = [];
14719         this.fileCollection.each(function(e) {
14720             data.push(e.data);
14721             
14722         });
14723         this.inputEl().dom.value = JSON.stringify(data);
14724         
14725         
14726         
14727     }
14728     
14729     
14730 });
14731
14732
14733 Roo.bootstrap.form.CardUploader.ID = -1;/*
14734  * Based on:
14735  * Ext JS Library 1.1.1
14736  * Copyright(c) 2006-2007, Ext JS, LLC.
14737  *
14738  * Originally Released Under LGPL - original licence link has changed is not relivant.
14739  *
14740  * Fork - LGPL
14741  * <script type="text/javascript">
14742  */
14743
14744
14745 /**
14746  * @class Roo.data.SortTypes
14747  * @static
14748  * Defines the default sorting (casting?) comparison functions used when sorting data.
14749  */
14750 Roo.data.SortTypes = {
14751     /**
14752      * Default sort that does nothing
14753      * @param {Mixed} s The value being converted
14754      * @return {Mixed} The comparison value
14755      */
14756     none : function(s){
14757         return s;
14758     },
14759     
14760     /**
14761      * The regular expression used to strip tags
14762      * @type {RegExp}
14763      * @property
14764      */
14765     stripTagsRE : /<\/?[^>]+>/gi,
14766     
14767     /**
14768      * Strips all HTML tags to sort on text only
14769      * @param {Mixed} s The value being converted
14770      * @return {String} The comparison value
14771      */
14772     asText : function(s){
14773         return String(s).replace(this.stripTagsRE, "");
14774     },
14775     
14776     /**
14777      * Strips all HTML tags to sort on text only - Case insensitive
14778      * @param {Mixed} s The value being converted
14779      * @return {String} The comparison value
14780      */
14781     asUCText : function(s){
14782         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14783     },
14784     
14785     /**
14786      * Case insensitive string
14787      * @param {Mixed} s The value being converted
14788      * @return {String} The comparison value
14789      */
14790     asUCString : function(s) {
14791         return String(s).toUpperCase();
14792     },
14793     
14794     /**
14795      * Date sorting
14796      * @param {Mixed} s The value being converted
14797      * @return {Number} The comparison value
14798      */
14799     asDate : function(s) {
14800         if(!s){
14801             return 0;
14802         }
14803         if(s instanceof Date){
14804             return s.getTime();
14805         }
14806         return Date.parse(String(s));
14807     },
14808     
14809     /**
14810      * Float sorting
14811      * @param {Mixed} s The value being converted
14812      * @return {Float} The comparison value
14813      */
14814     asFloat : function(s) {
14815         var val = parseFloat(String(s).replace(/,/g, ""));
14816         if(isNaN(val)) {
14817             val = 0;
14818         }
14819         return val;
14820     },
14821     
14822     /**
14823      * Integer sorting
14824      * @param {Mixed} s The value being converted
14825      * @return {Number} The comparison value
14826      */
14827     asInt : function(s) {
14828         var val = parseInt(String(s).replace(/,/g, ""));
14829         if(isNaN(val)) {
14830             val = 0;
14831         }
14832         return val;
14833     }
14834 };/*
14835  * Based on:
14836  * Ext JS Library 1.1.1
14837  * Copyright(c) 2006-2007, Ext JS, LLC.
14838  *
14839  * Originally Released Under LGPL - original licence link has changed is not relivant.
14840  *
14841  * Fork - LGPL
14842  * <script type="text/javascript">
14843  */
14844
14845 /**
14846 * @class Roo.data.Record
14847  * Instances of this class encapsulate both record <em>definition</em> information, and record
14848  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14849  * to access Records cached in an {@link Roo.data.Store} object.<br>
14850  * <p>
14851  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14852  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14853  * objects.<br>
14854  * <p>
14855  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14856  * @constructor
14857  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14858  * {@link #create}. The parameters are the same.
14859  * @param {Array} data An associative Array of data values keyed by the field name.
14860  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14861  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14862  * not specified an integer id is generated.
14863  */
14864 Roo.data.Record = function(data, id){
14865     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14866     this.data = data;
14867 };
14868
14869 /**
14870  * Generate a constructor for a specific record layout.
14871  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14872  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14873  * Each field definition object may contain the following properties: <ul>
14874  * <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,
14875  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14876  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14877  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14878  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14879  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14880  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14881  * this may be omitted.</p></li>
14882  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14883  * <ul><li>auto (Default, implies no conversion)</li>
14884  * <li>string</li>
14885  * <li>int</li>
14886  * <li>float</li>
14887  * <li>boolean</li>
14888  * <li>date</li></ul></p></li>
14889  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14890  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14891  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14892  * by the Reader into an object that will be stored in the Record. It is passed the
14893  * following parameters:<ul>
14894  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14895  * </ul></p></li>
14896  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14897  * </ul>
14898  * <br>usage:<br><pre><code>
14899 var TopicRecord = Roo.data.Record.create(
14900     {name: 'title', mapping: 'topic_title'},
14901     {name: 'author', mapping: 'username'},
14902     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14903     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14904     {name: 'lastPoster', mapping: 'user2'},
14905     {name: 'excerpt', mapping: 'post_text'}
14906 );
14907
14908 var myNewRecord = new TopicRecord({
14909     title: 'Do my job please',
14910     author: 'noobie',
14911     totalPosts: 1,
14912     lastPost: new Date(),
14913     lastPoster: 'Animal',
14914     excerpt: 'No way dude!'
14915 });
14916 myStore.add(myNewRecord);
14917 </code></pre>
14918  * @method create
14919  * @static
14920  */
14921 Roo.data.Record.create = function(o){
14922     var f = function(){
14923         f.superclass.constructor.apply(this, arguments);
14924     };
14925     Roo.extend(f, Roo.data.Record);
14926     var p = f.prototype;
14927     p.fields = new Roo.util.MixedCollection(false, function(field){
14928         return field.name;
14929     });
14930     for(var i = 0, len = o.length; i < len; i++){
14931         p.fields.add(new Roo.data.Field(o[i]));
14932     }
14933     f.getField = function(name){
14934         return p.fields.get(name);  
14935     };
14936     return f;
14937 };
14938
14939 Roo.data.Record.AUTO_ID = 1000;
14940 Roo.data.Record.EDIT = 'edit';
14941 Roo.data.Record.REJECT = 'reject';
14942 Roo.data.Record.COMMIT = 'commit';
14943
14944 Roo.data.Record.prototype = {
14945     /**
14946      * Readonly flag - true if this record has been modified.
14947      * @type Boolean
14948      */
14949     dirty : false,
14950     editing : false,
14951     error: null,
14952     modified: null,
14953
14954     // private
14955     join : function(store){
14956         this.store = store;
14957     },
14958
14959     /**
14960      * Set the named field to the specified value.
14961      * @param {String} name The name of the field to set.
14962      * @param {Object} value The value to set the field to.
14963      */
14964     set : function(name, value){
14965         if(this.data[name] == value){
14966             return;
14967         }
14968         this.dirty = true;
14969         if(!this.modified){
14970             this.modified = {};
14971         }
14972         if(typeof this.modified[name] == 'undefined'){
14973             this.modified[name] = this.data[name];
14974         }
14975         this.data[name] = value;
14976         if(!this.editing && this.store){
14977             this.store.afterEdit(this);
14978         }       
14979     },
14980
14981     /**
14982      * Get the value of the named field.
14983      * @param {String} name The name of the field to get the value of.
14984      * @return {Object} The value of the field.
14985      */
14986     get : function(name){
14987         return this.data[name]; 
14988     },
14989
14990     // private
14991     beginEdit : function(){
14992         this.editing = true;
14993         this.modified = {}; 
14994     },
14995
14996     // private
14997     cancelEdit : function(){
14998         this.editing = false;
14999         delete this.modified;
15000     },
15001
15002     // private
15003     endEdit : function(){
15004         this.editing = false;
15005         if(this.dirty && this.store){
15006             this.store.afterEdit(this);
15007         }
15008     },
15009
15010     /**
15011      * Usually called by the {@link Roo.data.Store} which owns the Record.
15012      * Rejects all changes made to the Record since either creation, or the last commit operation.
15013      * Modified fields are reverted to their original values.
15014      * <p>
15015      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15016      * of reject operations.
15017      */
15018     reject : function(){
15019         var m = this.modified;
15020         for(var n in m){
15021             if(typeof m[n] != "function"){
15022                 this.data[n] = m[n];
15023             }
15024         }
15025         this.dirty = false;
15026         delete this.modified;
15027         this.editing = false;
15028         if(this.store){
15029             this.store.afterReject(this);
15030         }
15031     },
15032
15033     /**
15034      * Usually called by the {@link Roo.data.Store} which owns the Record.
15035      * Commits all changes made to the Record since either creation, or the last commit operation.
15036      * <p>
15037      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15038      * of commit operations.
15039      */
15040     commit : function(){
15041         this.dirty = false;
15042         delete this.modified;
15043         this.editing = false;
15044         if(this.store){
15045             this.store.afterCommit(this);
15046         }
15047     },
15048
15049     // private
15050     hasError : function(){
15051         return this.error != null;
15052     },
15053
15054     // private
15055     clearError : function(){
15056         this.error = null;
15057     },
15058
15059     /**
15060      * Creates a copy of this record.
15061      * @param {String} id (optional) A new record id if you don't want to use this record's id
15062      * @return {Record}
15063      */
15064     copy : function(newId) {
15065         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15066     }
15067 };/*
15068  * Based on:
15069  * Ext JS Library 1.1.1
15070  * Copyright(c) 2006-2007, Ext JS, LLC.
15071  *
15072  * Originally Released Under LGPL - original licence link has changed is not relivant.
15073  *
15074  * Fork - LGPL
15075  * <script type="text/javascript">
15076  */
15077
15078
15079
15080 /**
15081  * @class Roo.data.Store
15082  * @extends Roo.util.Observable
15083  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15084  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15085  * <p>
15086  * 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
15087  * has no knowledge of the format of the data returned by the Proxy.<br>
15088  * <p>
15089  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15090  * instances from the data object. These records are cached and made available through accessor functions.
15091  * @constructor
15092  * Creates a new Store.
15093  * @param {Object} config A config object containing the objects needed for the Store to access data,
15094  * and read the data into Records.
15095  */
15096 Roo.data.Store = function(config){
15097     this.data = new Roo.util.MixedCollection(false);
15098     this.data.getKey = function(o){
15099         return o.id;
15100     };
15101     this.baseParams = {};
15102     // private
15103     this.paramNames = {
15104         "start" : "start",
15105         "limit" : "limit",
15106         "sort" : "sort",
15107         "dir" : "dir",
15108         "multisort" : "_multisort"
15109     };
15110
15111     if(config && config.data){
15112         this.inlineData = config.data;
15113         delete config.data;
15114     }
15115
15116     Roo.apply(this, config);
15117     
15118     if(this.reader){ // reader passed
15119         this.reader = Roo.factory(this.reader, Roo.data);
15120         this.reader.xmodule = this.xmodule || false;
15121         if(!this.recordType){
15122             this.recordType = this.reader.recordType;
15123         }
15124         if(this.reader.onMetaChange){
15125             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15126         }
15127     }
15128
15129     if(this.recordType){
15130         this.fields = this.recordType.prototype.fields;
15131     }
15132     this.modified = [];
15133
15134     this.addEvents({
15135         /**
15136          * @event datachanged
15137          * Fires when the data cache has changed, and a widget which is using this Store
15138          * as a Record cache should refresh its view.
15139          * @param {Store} this
15140          */
15141         datachanged : true,
15142         /**
15143          * @event metachange
15144          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15145          * @param {Store} this
15146          * @param {Object} meta The JSON metadata
15147          */
15148         metachange : true,
15149         /**
15150          * @event add
15151          * Fires when Records have been added to the Store
15152          * @param {Store} this
15153          * @param {Roo.data.Record[]} records The array of Records added
15154          * @param {Number} index The index at which the record(s) were added
15155          */
15156         add : true,
15157         /**
15158          * @event remove
15159          * Fires when a Record has been removed from the Store
15160          * @param {Store} this
15161          * @param {Roo.data.Record} record The Record that was removed
15162          * @param {Number} index The index at which the record was removed
15163          */
15164         remove : true,
15165         /**
15166          * @event update
15167          * Fires when a Record has been updated
15168          * @param {Store} this
15169          * @param {Roo.data.Record} record The Record that was updated
15170          * @param {String} operation The update operation being performed.  Value may be one of:
15171          * <pre><code>
15172  Roo.data.Record.EDIT
15173  Roo.data.Record.REJECT
15174  Roo.data.Record.COMMIT
15175          * </code></pre>
15176          */
15177         update : true,
15178         /**
15179          * @event clear
15180          * Fires when the data cache has been cleared.
15181          * @param {Store} this
15182          */
15183         clear : true,
15184         /**
15185          * @event beforeload
15186          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15187          * the load action will be canceled.
15188          * @param {Store} this
15189          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15190          */
15191         beforeload : true,
15192         /**
15193          * @event beforeloadadd
15194          * Fires after a new set of Records has been loaded.
15195          * @param {Store} this
15196          * @param {Roo.data.Record[]} records The Records that were loaded
15197          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15198          */
15199         beforeloadadd : true,
15200         /**
15201          * @event load
15202          * Fires after a new set of Records has been loaded, before they are added to the store.
15203          * @param {Store} this
15204          * @param {Roo.data.Record[]} records The Records that were loaded
15205          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15206          * @params {Object} return from reader
15207          */
15208         load : true,
15209         /**
15210          * @event loadexception
15211          * Fires if an exception occurs in the Proxy during loading.
15212          * Called with the signature of the Proxy's "loadexception" event.
15213          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15214          * 
15215          * @param {Proxy} 
15216          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15217          * @param {Object} load options 
15218          * @param {Object} jsonData from your request (normally this contains the Exception)
15219          */
15220         loadexception : true
15221     });
15222     
15223     if(this.proxy){
15224         this.proxy = Roo.factory(this.proxy, Roo.data);
15225         this.proxy.xmodule = this.xmodule || false;
15226         this.relayEvents(this.proxy,  ["loadexception"]);
15227     }
15228     this.sortToggle = {};
15229     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15230
15231     Roo.data.Store.superclass.constructor.call(this);
15232
15233     if(this.inlineData){
15234         this.loadData(this.inlineData);
15235         delete this.inlineData;
15236     }
15237 };
15238
15239 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15240      /**
15241     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15242     * without a remote query - used by combo/forms at present.
15243     */
15244     
15245     /**
15246     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15247     */
15248     /**
15249     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15250     */
15251     /**
15252     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15253     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15254     */
15255     /**
15256     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15257     * on any HTTP request
15258     */
15259     /**
15260     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15261     */
15262     /**
15263     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15264     */
15265     multiSort: false,
15266     /**
15267     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15268     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15269     */
15270     remoteSort : false,
15271
15272     /**
15273     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15274      * loaded or when a record is removed. (defaults to false).
15275     */
15276     pruneModifiedRecords : false,
15277
15278     // private
15279     lastOptions : null,
15280
15281     /**
15282      * Add Records to the Store and fires the add event.
15283      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15284      */
15285     add : function(records){
15286         records = [].concat(records);
15287         for(var i = 0, len = records.length; i < len; i++){
15288             records[i].join(this);
15289         }
15290         var index = this.data.length;
15291         this.data.addAll(records);
15292         this.fireEvent("add", this, records, index);
15293     },
15294
15295     /**
15296      * Remove a Record from the Store and fires the remove event.
15297      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15298      */
15299     remove : function(record){
15300         var index = this.data.indexOf(record);
15301         this.data.removeAt(index);
15302  
15303         if(this.pruneModifiedRecords){
15304             this.modified.remove(record);
15305         }
15306         this.fireEvent("remove", this, record, index);
15307     },
15308
15309     /**
15310      * Remove all Records from the Store and fires the clear event.
15311      */
15312     removeAll : function(){
15313         this.data.clear();
15314         if(this.pruneModifiedRecords){
15315             this.modified = [];
15316         }
15317         this.fireEvent("clear", this);
15318     },
15319
15320     /**
15321      * Inserts Records to the Store at the given index and fires the add event.
15322      * @param {Number} index The start index at which to insert the passed Records.
15323      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15324      */
15325     insert : function(index, records){
15326         records = [].concat(records);
15327         for(var i = 0, len = records.length; i < len; i++){
15328             this.data.insert(index, records[i]);
15329             records[i].join(this);
15330         }
15331         this.fireEvent("add", this, records, index);
15332     },
15333
15334     /**
15335      * Get the index within the cache of the passed Record.
15336      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15337      * @return {Number} The index of the passed Record. Returns -1 if not found.
15338      */
15339     indexOf : function(record){
15340         return this.data.indexOf(record);
15341     },
15342
15343     /**
15344      * Get the index within the cache of the Record with the passed id.
15345      * @param {String} id The id of the Record to find.
15346      * @return {Number} The index of the Record. Returns -1 if not found.
15347      */
15348     indexOfId : function(id){
15349         return this.data.indexOfKey(id);
15350     },
15351
15352     /**
15353      * Get the Record with the specified id.
15354      * @param {String} id The id of the Record to find.
15355      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15356      */
15357     getById : function(id){
15358         return this.data.key(id);
15359     },
15360
15361     /**
15362      * Get the Record at the specified index.
15363      * @param {Number} index The index of the Record to find.
15364      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15365      */
15366     getAt : function(index){
15367         return this.data.itemAt(index);
15368     },
15369
15370     /**
15371      * Returns a range of Records between specified indices.
15372      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15373      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15374      * @return {Roo.data.Record[]} An array of Records
15375      */
15376     getRange : function(start, end){
15377         return this.data.getRange(start, end);
15378     },
15379
15380     // private
15381     storeOptions : function(o){
15382         o = Roo.apply({}, o);
15383         delete o.callback;
15384         delete o.scope;
15385         this.lastOptions = o;
15386     },
15387
15388     /**
15389      * Loads the Record cache from the configured Proxy using the configured Reader.
15390      * <p>
15391      * If using remote paging, then the first load call must specify the <em>start</em>
15392      * and <em>limit</em> properties in the options.params property to establish the initial
15393      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15394      * <p>
15395      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15396      * and this call will return before the new data has been loaded. Perform any post-processing
15397      * in a callback function, or in a "load" event handler.</strong>
15398      * <p>
15399      * @param {Object} options An object containing properties which control loading options:<ul>
15400      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15401      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15402      * <pre>
15403                 {
15404                     data : data,  // array of key=>value data like JsonReader
15405                     total : data.length,
15406                     success : true
15407                     
15408                 }
15409         </pre>
15410             }.</li>
15411      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15412      * passed the following arguments:<ul>
15413      * <li>r : Roo.data.Record[]</li>
15414      * <li>options: Options object from the load call</li>
15415      * <li>success: Boolean success indicator</li></ul></li>
15416      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15417      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15418      * </ul>
15419      */
15420     load : function(options){
15421         options = options || {};
15422         if(this.fireEvent("beforeload", this, options) !== false){
15423             this.storeOptions(options);
15424             var p = Roo.apply(options.params || {}, this.baseParams);
15425             // if meta was not loaded from remote source.. try requesting it.
15426             if (!this.reader.metaFromRemote) {
15427                 p._requestMeta = 1;
15428             }
15429             if(this.sortInfo && this.remoteSort){
15430                 var pn = this.paramNames;
15431                 p[pn["sort"]] = this.sortInfo.field;
15432                 p[pn["dir"]] = this.sortInfo.direction;
15433             }
15434             if (this.multiSort) {
15435                 var pn = this.paramNames;
15436                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15437             }
15438             
15439             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15440         }
15441     },
15442
15443     /**
15444      * Reloads the Record cache from the configured Proxy using the configured Reader and
15445      * the options from the last load operation performed.
15446      * @param {Object} options (optional) An object containing properties which may override the options
15447      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15448      * the most recently used options are reused).
15449      */
15450     reload : function(options){
15451         this.load(Roo.applyIf(options||{}, this.lastOptions));
15452     },
15453
15454     // private
15455     // Called as a callback by the Reader during a load operation.
15456     loadRecords : function(o, options, success){
15457          
15458         if(!o){
15459             if(success !== false){
15460                 this.fireEvent("load", this, [], options, o);
15461             }
15462             if(options.callback){
15463                 options.callback.call(options.scope || this, [], options, false);
15464             }
15465             return;
15466         }
15467         // if data returned failure - throw an exception.
15468         if (o.success === false) {
15469             // show a message if no listener is registered.
15470             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15471                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15472             }
15473             // loadmask wil be hooked into this..
15474             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15475             return;
15476         }
15477         var r = o.records, t = o.totalRecords || r.length;
15478         
15479         this.fireEvent("beforeloadadd", this, r, options, o);
15480         
15481         if(!options || options.add !== true){
15482             if(this.pruneModifiedRecords){
15483                 this.modified = [];
15484             }
15485             for(var i = 0, len = r.length; i < len; i++){
15486                 r[i].join(this);
15487             }
15488             if(this.snapshot){
15489                 this.data = this.snapshot;
15490                 delete this.snapshot;
15491             }
15492             this.data.clear();
15493             this.data.addAll(r);
15494             this.totalLength = t;
15495             this.applySort();
15496             this.fireEvent("datachanged", this);
15497         }else{
15498             this.totalLength = Math.max(t, this.data.length+r.length);
15499             this.add(r);
15500         }
15501         
15502         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15503                 
15504             var e = new Roo.data.Record({});
15505
15506             e.set(this.parent.displayField, this.parent.emptyTitle);
15507             e.set(this.parent.valueField, '');
15508
15509             this.insert(0, e);
15510         }
15511             
15512         this.fireEvent("load", this, r, options, o);
15513         if(options.callback){
15514             options.callback.call(options.scope || this, r, options, true);
15515         }
15516     },
15517
15518
15519     /**
15520      * Loads data from a passed data block. A Reader which understands the format of the data
15521      * must have been configured in the constructor.
15522      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15523      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15524      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15525      */
15526     loadData : function(o, append){
15527         var r = this.reader.readRecords(o);
15528         this.loadRecords(r, {add: append}, true);
15529     },
15530     
15531      /**
15532      * using 'cn' the nested child reader read the child array into it's child stores.
15533      * @param {Object} rec The record with a 'children array
15534      */
15535     loadDataFromChildren : function(rec)
15536     {
15537         this.loadData(this.reader.toLoadData(rec));
15538     },
15539     
15540
15541     /**
15542      * Gets the number of cached records.
15543      * <p>
15544      * <em>If using paging, this may not be the total size of the dataset. If the data object
15545      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15546      * the data set size</em>
15547      */
15548     getCount : function(){
15549         return this.data.length || 0;
15550     },
15551
15552     /**
15553      * Gets the total number of records in the dataset as returned by the server.
15554      * <p>
15555      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15556      * the dataset size</em>
15557      */
15558     getTotalCount : function(){
15559         return this.totalLength || 0;
15560     },
15561
15562     /**
15563      * Returns the sort state of the Store as an object with two properties:
15564      * <pre><code>
15565  field {String} The name of the field by which the Records are sorted
15566  direction {String} The sort order, "ASC" or "DESC"
15567      * </code></pre>
15568      */
15569     getSortState : function(){
15570         return this.sortInfo;
15571     },
15572
15573     // private
15574     applySort : function(){
15575         if(this.sortInfo && !this.remoteSort){
15576             var s = this.sortInfo, f = s.field;
15577             var st = this.fields.get(f).sortType;
15578             var fn = function(r1, r2){
15579                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15580                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15581             };
15582             this.data.sort(s.direction, fn);
15583             if(this.snapshot && this.snapshot != this.data){
15584                 this.snapshot.sort(s.direction, fn);
15585             }
15586         }
15587     },
15588
15589     /**
15590      * Sets the default sort column and order to be used by the next load operation.
15591      * @param {String} fieldName The name of the field to sort by.
15592      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15593      */
15594     setDefaultSort : function(field, dir){
15595         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15596     },
15597
15598     /**
15599      * Sort the Records.
15600      * If remote sorting is used, the sort is performed on the server, and the cache is
15601      * reloaded. If local sorting is used, the cache is sorted internally.
15602      * @param {String} fieldName The name of the field to sort by.
15603      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15604      */
15605     sort : function(fieldName, dir){
15606         var f = this.fields.get(fieldName);
15607         if(!dir){
15608             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15609             
15610             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15611                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15612             }else{
15613                 dir = f.sortDir;
15614             }
15615         }
15616         this.sortToggle[f.name] = dir;
15617         this.sortInfo = {field: f.name, direction: dir};
15618         if(!this.remoteSort){
15619             this.applySort();
15620             this.fireEvent("datachanged", this);
15621         }else{
15622             this.load(this.lastOptions);
15623         }
15624     },
15625
15626     /**
15627      * Calls the specified function for each of the Records in the cache.
15628      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15629      * Returning <em>false</em> aborts and exits the iteration.
15630      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15631      */
15632     each : function(fn, scope){
15633         this.data.each(fn, scope);
15634     },
15635
15636     /**
15637      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15638      * (e.g., during paging).
15639      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15640      */
15641     getModifiedRecords : function(){
15642         return this.modified;
15643     },
15644
15645     // private
15646     createFilterFn : function(property, value, anyMatch){
15647         if(!value.exec){ // not a regex
15648             value = String(value);
15649             if(value.length == 0){
15650                 return false;
15651             }
15652             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15653         }
15654         return function(r){
15655             return value.test(r.data[property]);
15656         };
15657     },
15658
15659     /**
15660      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15661      * @param {String} property A field on your records
15662      * @param {Number} start The record index to start at (defaults to 0)
15663      * @param {Number} end The last record index to include (defaults to length - 1)
15664      * @return {Number} The sum
15665      */
15666     sum : function(property, start, end){
15667         var rs = this.data.items, v = 0;
15668         start = start || 0;
15669         end = (end || end === 0) ? end : rs.length-1;
15670
15671         for(var i = start; i <= end; i++){
15672             v += (rs[i].data[property] || 0);
15673         }
15674         return v;
15675     },
15676
15677     /**
15678      * Filter the records by a specified property.
15679      * @param {String} field A field on your records
15680      * @param {String/RegExp} value Either a string that the field
15681      * should start with or a RegExp to test against the field
15682      * @param {Boolean} anyMatch True to match any part not just the beginning
15683      */
15684     filter : function(property, value, anyMatch){
15685         var fn = this.createFilterFn(property, value, anyMatch);
15686         return fn ? this.filterBy(fn) : this.clearFilter();
15687     },
15688
15689     /**
15690      * Filter by a function. The specified function will be called with each
15691      * record in this data source. If the function returns true the record is included,
15692      * otherwise it is filtered.
15693      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15694      * @param {Object} scope (optional) The scope of the function (defaults to this)
15695      */
15696     filterBy : function(fn, scope){
15697         this.snapshot = this.snapshot || this.data;
15698         this.data = this.queryBy(fn, scope||this);
15699         this.fireEvent("datachanged", this);
15700     },
15701
15702     /**
15703      * Query the records by a specified property.
15704      * @param {String} field A field on your records
15705      * @param {String/RegExp} value Either a string that the field
15706      * should start with or a RegExp to test against the field
15707      * @param {Boolean} anyMatch True to match any part not just the beginning
15708      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15709      */
15710     query : function(property, value, anyMatch){
15711         var fn = this.createFilterFn(property, value, anyMatch);
15712         return fn ? this.queryBy(fn) : this.data.clone();
15713     },
15714
15715     /**
15716      * Query by a function. The specified function will be called with each
15717      * record in this data source. If the function returns true the record is included
15718      * in the results.
15719      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15720      * @param {Object} scope (optional) The scope of the function (defaults to this)
15721       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15722      **/
15723     queryBy : function(fn, scope){
15724         var data = this.snapshot || this.data;
15725         return data.filterBy(fn, scope||this);
15726     },
15727
15728     /**
15729      * Collects unique values for a particular dataIndex from this store.
15730      * @param {String} dataIndex The property to collect
15731      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15732      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15733      * @return {Array} An array of the unique values
15734      **/
15735     collect : function(dataIndex, allowNull, bypassFilter){
15736         var d = (bypassFilter === true && this.snapshot) ?
15737                 this.snapshot.items : this.data.items;
15738         var v, sv, r = [], l = {};
15739         for(var i = 0, len = d.length; i < len; i++){
15740             v = d[i].data[dataIndex];
15741             sv = String(v);
15742             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15743                 l[sv] = true;
15744                 r[r.length] = v;
15745             }
15746         }
15747         return r;
15748     },
15749
15750     /**
15751      * Revert to a view of the Record cache with no filtering applied.
15752      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15753      */
15754     clearFilter : function(suppressEvent){
15755         if(this.snapshot && this.snapshot != this.data){
15756             this.data = this.snapshot;
15757             delete this.snapshot;
15758             if(suppressEvent !== true){
15759                 this.fireEvent("datachanged", this);
15760             }
15761         }
15762     },
15763
15764     // private
15765     afterEdit : function(record){
15766         if(this.modified.indexOf(record) == -1){
15767             this.modified.push(record);
15768         }
15769         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15770     },
15771     
15772     // private
15773     afterReject : function(record){
15774         this.modified.remove(record);
15775         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15776     },
15777
15778     // private
15779     afterCommit : function(record){
15780         this.modified.remove(record);
15781         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15782     },
15783
15784     /**
15785      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15786      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15787      */
15788     commitChanges : function(){
15789         var m = this.modified.slice(0);
15790         this.modified = [];
15791         for(var i = 0, len = m.length; i < len; i++){
15792             m[i].commit();
15793         }
15794     },
15795
15796     /**
15797      * Cancel outstanding changes on all changed records.
15798      */
15799     rejectChanges : function(){
15800         var m = this.modified.slice(0);
15801         this.modified = [];
15802         for(var i = 0, len = m.length; i < len; i++){
15803             m[i].reject();
15804         }
15805     },
15806
15807     onMetaChange : function(meta, rtype, o){
15808         this.recordType = rtype;
15809         this.fields = rtype.prototype.fields;
15810         delete this.snapshot;
15811         this.sortInfo = meta.sortInfo || this.sortInfo;
15812         this.modified = [];
15813         this.fireEvent('metachange', this, this.reader.meta);
15814     },
15815     
15816     moveIndex : function(data, type)
15817     {
15818         var index = this.indexOf(data);
15819         
15820         var newIndex = index + type;
15821         
15822         this.remove(data);
15823         
15824         this.insert(newIndex, data);
15825         
15826     }
15827 });/*
15828  * Based on:
15829  * Ext JS Library 1.1.1
15830  * Copyright(c) 2006-2007, Ext JS, LLC.
15831  *
15832  * Originally Released Under LGPL - original licence link has changed is not relivant.
15833  *
15834  * Fork - LGPL
15835  * <script type="text/javascript">
15836  */
15837
15838 /**
15839  * @class Roo.data.SimpleStore
15840  * @extends Roo.data.Store
15841  * Small helper class to make creating Stores from Array data easier.
15842  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15843  * @cfg {Array} fields An array of field definition objects, or field name strings.
15844  * @cfg {Object} an existing reader (eg. copied from another store)
15845  * @cfg {Array} data The multi-dimensional array of data
15846  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15847  * @cfg {Roo.data.Reader} reader  [not-required] 
15848  * @constructor
15849  * @param {Object} config
15850  */
15851 Roo.data.SimpleStore = function(config)
15852 {
15853     Roo.data.SimpleStore.superclass.constructor.call(this, {
15854         isLocal : true,
15855         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15856                 id: config.id
15857             },
15858             Roo.data.Record.create(config.fields)
15859         ),
15860         proxy : new Roo.data.MemoryProxy(config.data)
15861     });
15862     this.load();
15863 };
15864 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15865  * Based on:
15866  * Ext JS Library 1.1.1
15867  * Copyright(c) 2006-2007, Ext JS, LLC.
15868  *
15869  * Originally Released Under LGPL - original licence link has changed is not relivant.
15870  *
15871  * Fork - LGPL
15872  * <script type="text/javascript">
15873  */
15874
15875 /**
15876 /**
15877  * @extends Roo.data.Store
15878  * @class Roo.data.JsonStore
15879  * Small helper class to make creating Stores for JSON data easier. <br/>
15880 <pre><code>
15881 var store = new Roo.data.JsonStore({
15882     url: 'get-images.php',
15883     root: 'images',
15884     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15885 });
15886 </code></pre>
15887  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15888  * JsonReader and HttpProxy (unless inline data is provided).</b>
15889  * @cfg {Array} fields An array of field definition objects, or field name strings.
15890  * @constructor
15891  * @param {Object} config
15892  */
15893 Roo.data.JsonStore = function(c){
15894     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15895         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15896         reader: new Roo.data.JsonReader(c, c.fields)
15897     }));
15898 };
15899 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15900  * Based on:
15901  * Ext JS Library 1.1.1
15902  * Copyright(c) 2006-2007, Ext JS, LLC.
15903  *
15904  * Originally Released Under LGPL - original licence link has changed is not relivant.
15905  *
15906  * Fork - LGPL
15907  * <script type="text/javascript">
15908  */
15909
15910  
15911 Roo.data.Field = function(config){
15912     if(typeof config == "string"){
15913         config = {name: config};
15914     }
15915     Roo.apply(this, config);
15916     
15917     if(!this.type){
15918         this.type = "auto";
15919     }
15920     
15921     var st = Roo.data.SortTypes;
15922     // named sortTypes are supported, here we look them up
15923     if(typeof this.sortType == "string"){
15924         this.sortType = st[this.sortType];
15925     }
15926     
15927     // set default sortType for strings and dates
15928     if(!this.sortType){
15929         switch(this.type){
15930             case "string":
15931                 this.sortType = st.asUCString;
15932                 break;
15933             case "date":
15934                 this.sortType = st.asDate;
15935                 break;
15936             default:
15937                 this.sortType = st.none;
15938         }
15939     }
15940
15941     // define once
15942     var stripRe = /[\$,%]/g;
15943
15944     // prebuilt conversion function for this field, instead of
15945     // switching every time we're reading a value
15946     if(!this.convert){
15947         var cv, dateFormat = this.dateFormat;
15948         switch(this.type){
15949             case "":
15950             case "auto":
15951             case undefined:
15952                 cv = function(v){ return v; };
15953                 break;
15954             case "string":
15955                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15956                 break;
15957             case "int":
15958                 cv = function(v){
15959                     return v !== undefined && v !== null && v !== '' ?
15960                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15961                     };
15962                 break;
15963             case "float":
15964                 cv = function(v){
15965                     return v !== undefined && v !== null && v !== '' ?
15966                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15967                     };
15968                 break;
15969             case "bool":
15970             case "boolean":
15971                 cv = function(v){ return v === true || v === "true" || v == 1; };
15972                 break;
15973             case "date":
15974                 cv = function(v){
15975                     if(!v){
15976                         return '';
15977                     }
15978                     if(v instanceof Date){
15979                         return v;
15980                     }
15981                     if(dateFormat){
15982                         if(dateFormat == "timestamp"){
15983                             return new Date(v*1000);
15984                         }
15985                         return Date.parseDate(v, dateFormat);
15986                     }
15987                     var parsed = Date.parse(v);
15988                     return parsed ? new Date(parsed) : null;
15989                 };
15990              break;
15991             
15992         }
15993         this.convert = cv;
15994     }
15995 };
15996
15997 Roo.data.Field.prototype = {
15998     dateFormat: null,
15999     defaultValue: "",
16000     mapping: null,
16001     sortType : null,
16002     sortDir : "ASC"
16003 };/*
16004  * Based on:
16005  * Ext JS Library 1.1.1
16006  * Copyright(c) 2006-2007, Ext JS, LLC.
16007  *
16008  * Originally Released Under LGPL - original licence link has changed is not relivant.
16009  *
16010  * Fork - LGPL
16011  * <script type="text/javascript">
16012  */
16013  
16014 // Base class for reading structured data from a data source.  This class is intended to be
16015 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
16016
16017 /**
16018  * @class Roo.data.DataReader
16019  * @abstract
16020  * Base class for reading structured data from a data source.  This class is intended to be
16021  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16022  */
16023
16024 Roo.data.DataReader = function(meta, recordType){
16025     
16026     this.meta = meta;
16027     
16028     this.recordType = recordType instanceof Array ? 
16029         Roo.data.Record.create(recordType) : recordType;
16030 };
16031
16032 Roo.data.DataReader.prototype = {
16033     
16034     
16035     readerType : 'Data',
16036      /**
16037      * Create an empty record
16038      * @param {Object} data (optional) - overlay some values
16039      * @return {Roo.data.Record} record created.
16040      */
16041     newRow :  function(d) {
16042         var da =  {};
16043         this.recordType.prototype.fields.each(function(c) {
16044             switch( c.type) {
16045                 case 'int' : da[c.name] = 0; break;
16046                 case 'date' : da[c.name] = new Date(); break;
16047                 case 'float' : da[c.name] = 0.0; break;
16048                 case 'boolean' : da[c.name] = false; break;
16049                 default : da[c.name] = ""; break;
16050             }
16051             
16052         });
16053         return new this.recordType(Roo.apply(da, d));
16054     }
16055     
16056     
16057 };/*
16058  * Based on:
16059  * Ext JS Library 1.1.1
16060  * Copyright(c) 2006-2007, Ext JS, LLC.
16061  *
16062  * Originally Released Under LGPL - original licence link has changed is not relivant.
16063  *
16064  * Fork - LGPL
16065  * <script type="text/javascript">
16066  */
16067
16068 /**
16069  * @class Roo.data.DataProxy
16070  * @extends Roo.util.Observable
16071  * @abstract
16072  * This class is an abstract base class for implementations which provide retrieval of
16073  * unformatted data objects.<br>
16074  * <p>
16075  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16076  * (of the appropriate type which knows how to parse the data object) to provide a block of
16077  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16078  * <p>
16079  * Custom implementations must implement the load method as described in
16080  * {@link Roo.data.HttpProxy#load}.
16081  */
16082 Roo.data.DataProxy = function(){
16083     this.addEvents({
16084         /**
16085          * @event beforeload
16086          * Fires before a network request is made to retrieve a data object.
16087          * @param {Object} This DataProxy object.
16088          * @param {Object} params The params parameter to the load function.
16089          */
16090         beforeload : true,
16091         /**
16092          * @event load
16093          * Fires before the load method's callback is called.
16094          * @param {Object} This DataProxy object.
16095          * @param {Object} o The data object.
16096          * @param {Object} arg The callback argument object passed to the load function.
16097          */
16098         load : true,
16099         /**
16100          * @event loadexception
16101          * Fires if an Exception occurs during data retrieval.
16102          * @param {Object} This DataProxy object.
16103          * @param {Object} o The data object.
16104          * @param {Object} arg The callback argument object passed to the load function.
16105          * @param {Object} e The Exception.
16106          */
16107         loadexception : true
16108     });
16109     Roo.data.DataProxy.superclass.constructor.call(this);
16110 };
16111
16112 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16113
16114     /**
16115      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16116      */
16117 /*
16118  * Based on:
16119  * Ext JS Library 1.1.1
16120  * Copyright(c) 2006-2007, Ext JS, LLC.
16121  *
16122  * Originally Released Under LGPL - original licence link has changed is not relivant.
16123  *
16124  * Fork - LGPL
16125  * <script type="text/javascript">
16126  */
16127 /**
16128  * @class Roo.data.MemoryProxy
16129  * @extends Roo.data.DataProxy
16130  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16131  * to the Reader when its load method is called.
16132  * @constructor
16133  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16134  */
16135 Roo.data.MemoryProxy = function(config){
16136     var data = config;
16137     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16138         data = config.data;
16139     }
16140     Roo.data.MemoryProxy.superclass.constructor.call(this);
16141     this.data = data;
16142 };
16143
16144 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16145     
16146     /**
16147      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16148      */
16149     /**
16150      * Load data from the requested source (in this case an in-memory
16151      * data object passed to the constructor), read the data object into
16152      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16153      * process that block using the passed callback.
16154      * @param {Object} params This parameter is not used by the MemoryProxy class.
16155      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16156      * object into a block of Roo.data.Records.
16157      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16158      * The function must be passed <ul>
16159      * <li>The Record block object</li>
16160      * <li>The "arg" argument from the load function</li>
16161      * <li>A boolean success indicator</li>
16162      * </ul>
16163      * @param {Object} scope The scope in which to call the callback
16164      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16165      */
16166     load : function(params, reader, callback, scope, arg){
16167         params = params || {};
16168         var result;
16169         try {
16170             result = reader.readRecords(params.data ? params.data :this.data);
16171         }catch(e){
16172             this.fireEvent("loadexception", this, arg, null, e);
16173             callback.call(scope, null, arg, false);
16174             return;
16175         }
16176         callback.call(scope, result, arg, true);
16177     },
16178     
16179     // private
16180     update : function(params, records){
16181         
16182     }
16183 });/*
16184  * Based on:
16185  * Ext JS Library 1.1.1
16186  * Copyright(c) 2006-2007, Ext JS, LLC.
16187  *
16188  * Originally Released Under LGPL - original licence link has changed is not relivant.
16189  *
16190  * Fork - LGPL
16191  * <script type="text/javascript">
16192  */
16193 /**
16194  * @class Roo.data.HttpProxy
16195  * @extends Roo.data.DataProxy
16196  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16197  * configured to reference a certain URL.<br><br>
16198  * <p>
16199  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16200  * from which the running page was served.<br><br>
16201  * <p>
16202  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16203  * <p>
16204  * Be aware that to enable the browser to parse an XML document, the server must set
16205  * the Content-Type header in the HTTP response to "text/xml".
16206  * @constructor
16207  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16208  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16209  * will be used to make the request.
16210  */
16211 Roo.data.HttpProxy = function(conn){
16212     Roo.data.HttpProxy.superclass.constructor.call(this);
16213     // is conn a conn config or a real conn?
16214     this.conn = conn;
16215     this.useAjax = !conn || !conn.events;
16216   
16217 };
16218
16219 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16220     // thse are take from connection...
16221     
16222     /**
16223      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16224      */
16225     /**
16226      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16227      * extra parameters to each request made by this object. (defaults to undefined)
16228      */
16229     /**
16230      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16231      *  to each request made by this object. (defaults to undefined)
16232      */
16233     /**
16234      * @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)
16235      */
16236     /**
16237      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16238      */
16239      /**
16240      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16241      * @type Boolean
16242      */
16243   
16244
16245     /**
16246      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16247      * @type Boolean
16248      */
16249     /**
16250      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16251      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16252      * a finer-grained basis than the DataProxy events.
16253      */
16254     getConnection : function(){
16255         return this.useAjax ? Roo.Ajax : this.conn;
16256     },
16257
16258     /**
16259      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16260      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16261      * process that block using the passed callback.
16262      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16263      * for the request to the remote server.
16264      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16265      * object into a block of Roo.data.Records.
16266      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16267      * The function must be passed <ul>
16268      * <li>The Record block object</li>
16269      * <li>The "arg" argument from the load function</li>
16270      * <li>A boolean success indicator</li>
16271      * </ul>
16272      * @param {Object} scope The scope in which to call the callback
16273      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16274      */
16275     load : function(params, reader, callback, scope, arg){
16276         if(this.fireEvent("beforeload", this, params) !== false){
16277             var  o = {
16278                 params : params || {},
16279                 request: {
16280                     callback : callback,
16281                     scope : scope,
16282                     arg : arg
16283                 },
16284                 reader: reader,
16285                 callback : this.loadResponse,
16286                 scope: this
16287             };
16288             if(this.useAjax){
16289                 Roo.applyIf(o, this.conn);
16290                 if(this.activeRequest){
16291                     Roo.Ajax.abort(this.activeRequest);
16292                 }
16293                 this.activeRequest = Roo.Ajax.request(o);
16294             }else{
16295                 this.conn.request(o);
16296             }
16297         }else{
16298             callback.call(scope||this, null, arg, false);
16299         }
16300     },
16301
16302     // private
16303     loadResponse : function(o, success, response){
16304         delete this.activeRequest;
16305         if(!success){
16306             this.fireEvent("loadexception", this, o, response);
16307             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16308             return;
16309         }
16310         var result;
16311         try {
16312             result = o.reader.read(response);
16313         }catch(e){
16314             o.success = false;
16315             o.raw = { errorMsg : response.responseText };
16316             this.fireEvent("loadexception", this, o, response, e);
16317             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16318             return;
16319         }
16320         
16321         this.fireEvent("load", this, o, o.request.arg);
16322         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16323     },
16324
16325     // private
16326     update : function(dataSet){
16327
16328     },
16329
16330     // private
16331     updateResponse : function(dataSet){
16332
16333     }
16334 });/*
16335  * Based on:
16336  * Ext JS Library 1.1.1
16337  * Copyright(c) 2006-2007, Ext JS, LLC.
16338  *
16339  * Originally Released Under LGPL - original licence link has changed is not relivant.
16340  *
16341  * Fork - LGPL
16342  * <script type="text/javascript">
16343  */
16344
16345 /**
16346  * @class Roo.data.ScriptTagProxy
16347  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16348  * other than the originating domain of the running page.<br><br>
16349  * <p>
16350  * <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
16351  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16352  * <p>
16353  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16354  * source code that is used as the source inside a &lt;script> tag.<br><br>
16355  * <p>
16356  * In order for the browser to process the returned data, the server must wrap the data object
16357  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16358  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16359  * depending on whether the callback name was passed:
16360  * <p>
16361  * <pre><code>
16362 boolean scriptTag = false;
16363 String cb = request.getParameter("callback");
16364 if (cb != null) {
16365     scriptTag = true;
16366     response.setContentType("text/javascript");
16367 } else {
16368     response.setContentType("application/x-json");
16369 }
16370 Writer out = response.getWriter();
16371 if (scriptTag) {
16372     out.write(cb + "(");
16373 }
16374 out.print(dataBlock.toJsonString());
16375 if (scriptTag) {
16376     out.write(");");
16377 }
16378 </pre></code>
16379  *
16380  * @constructor
16381  * @param {Object} config A configuration object.
16382  */
16383 Roo.data.ScriptTagProxy = function(config){
16384     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16385     Roo.apply(this, config);
16386     this.head = document.getElementsByTagName("head")[0];
16387 };
16388
16389 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16390
16391 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16392     /**
16393      * @cfg {String} url The URL from which to request the data object.
16394      */
16395     /**
16396      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16397      */
16398     timeout : 30000,
16399     /**
16400      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16401      * the server the name of the callback function set up by the load call to process the returned data object.
16402      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16403      * javascript output which calls this named function passing the data object as its only parameter.
16404      */
16405     callbackParam : "callback",
16406     /**
16407      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16408      * name to the request.
16409      */
16410     nocache : true,
16411
16412     /**
16413      * Load data from the configured URL, read the data object into
16414      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16415      * process that block using the passed callback.
16416      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16417      * for the request to the remote server.
16418      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16419      * object into a block of Roo.data.Records.
16420      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16421      * The function must be passed <ul>
16422      * <li>The Record block object</li>
16423      * <li>The "arg" argument from the load function</li>
16424      * <li>A boolean success indicator</li>
16425      * </ul>
16426      * @param {Object} scope The scope in which to call the callback
16427      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16428      */
16429     load : function(params, reader, callback, scope, arg){
16430         if(this.fireEvent("beforeload", this, params) !== false){
16431
16432             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16433
16434             var url = this.url;
16435             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16436             if(this.nocache){
16437                 url += "&_dc=" + (new Date().getTime());
16438             }
16439             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16440             var trans = {
16441                 id : transId,
16442                 cb : "stcCallback"+transId,
16443                 scriptId : "stcScript"+transId,
16444                 params : params,
16445                 arg : arg,
16446                 url : url,
16447                 callback : callback,
16448                 scope : scope,
16449                 reader : reader
16450             };
16451             var conn = this;
16452
16453             window[trans.cb] = function(o){
16454                 conn.handleResponse(o, trans);
16455             };
16456
16457             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16458
16459             if(this.autoAbort !== false){
16460                 this.abort();
16461             }
16462
16463             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16464
16465             var script = document.createElement("script");
16466             script.setAttribute("src", url);
16467             script.setAttribute("type", "text/javascript");
16468             script.setAttribute("id", trans.scriptId);
16469             this.head.appendChild(script);
16470
16471             this.trans = trans;
16472         }else{
16473             callback.call(scope||this, null, arg, false);
16474         }
16475     },
16476
16477     // private
16478     isLoading : function(){
16479         return this.trans ? true : false;
16480     },
16481
16482     /**
16483      * Abort the current server request.
16484      */
16485     abort : function(){
16486         if(this.isLoading()){
16487             this.destroyTrans(this.trans);
16488         }
16489     },
16490
16491     // private
16492     destroyTrans : function(trans, isLoaded){
16493         this.head.removeChild(document.getElementById(trans.scriptId));
16494         clearTimeout(trans.timeoutId);
16495         if(isLoaded){
16496             window[trans.cb] = undefined;
16497             try{
16498                 delete window[trans.cb];
16499             }catch(e){}
16500         }else{
16501             // if hasn't been loaded, wait for load to remove it to prevent script error
16502             window[trans.cb] = function(){
16503                 window[trans.cb] = undefined;
16504                 try{
16505                     delete window[trans.cb];
16506                 }catch(e){}
16507             };
16508         }
16509     },
16510
16511     // private
16512     handleResponse : function(o, trans){
16513         this.trans = false;
16514         this.destroyTrans(trans, true);
16515         var result;
16516         try {
16517             result = trans.reader.readRecords(o);
16518         }catch(e){
16519             this.fireEvent("loadexception", this, o, trans.arg, e);
16520             trans.callback.call(trans.scope||window, null, trans.arg, false);
16521             return;
16522         }
16523         this.fireEvent("load", this, o, trans.arg);
16524         trans.callback.call(trans.scope||window, result, trans.arg, true);
16525     },
16526
16527     // private
16528     handleFailure : function(trans){
16529         this.trans = false;
16530         this.destroyTrans(trans, false);
16531         this.fireEvent("loadexception", this, null, trans.arg);
16532         trans.callback.call(trans.scope||window, null, trans.arg, false);
16533     }
16534 });/*
16535  * Based on:
16536  * Ext JS Library 1.1.1
16537  * Copyright(c) 2006-2007, Ext JS, LLC.
16538  *
16539  * Originally Released Under LGPL - original licence link has changed is not relivant.
16540  *
16541  * Fork - LGPL
16542  * <script type="text/javascript">
16543  */
16544
16545 /**
16546  * @class Roo.data.JsonReader
16547  * @extends Roo.data.DataReader
16548  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16549  * based on mappings in a provided Roo.data.Record constructor.
16550  * 
16551  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16552  * in the reply previously. 
16553  * 
16554  * <p>
16555  * Example code:
16556  * <pre><code>
16557 var RecordDef = Roo.data.Record.create([
16558     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16559     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16560 ]);
16561 var myReader = new Roo.data.JsonReader({
16562     totalProperty: "results",    // The property which contains the total dataset size (optional)
16563     root: "rows",                // The property which contains an Array of row objects
16564     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16565 }, RecordDef);
16566 </code></pre>
16567  * <p>
16568  * This would consume a JSON file like this:
16569  * <pre><code>
16570 { 'results': 2, 'rows': [
16571     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16572     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16573 }
16574 </code></pre>
16575  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16576  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16577  * paged from the remote server.
16578  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16579  * @cfg {String} root name of the property which contains the Array of row objects.
16580  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16581  * @cfg {Array} fields Array of field definition objects
16582  * @constructor
16583  * Create a new JsonReader
16584  * @param {Object} meta Metadata configuration options
16585  * @param {Object} recordType Either an Array of field definition objects,
16586  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16587  */
16588 Roo.data.JsonReader = function(meta, recordType){
16589     
16590     meta = meta || {};
16591     // set some defaults:
16592     Roo.applyIf(meta, {
16593         totalProperty: 'total',
16594         successProperty : 'success',
16595         root : 'data',
16596         id : 'id'
16597     });
16598     
16599     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16600 };
16601 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16602     
16603     readerType : 'Json',
16604     
16605     /**
16606      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16607      * Used by Store query builder to append _requestMeta to params.
16608      * 
16609      */
16610     metaFromRemote : false,
16611     /**
16612      * This method is only used by a DataProxy which has retrieved data from a remote server.
16613      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16614      * @return {Object} data A data block which is used by an Roo.data.Store object as
16615      * a cache of Roo.data.Records.
16616      */
16617     read : function(response){
16618         var json = response.responseText;
16619        
16620         var o = /* eval:var:o */ eval("("+json+")");
16621         if(!o) {
16622             throw {message: "JsonReader.read: Json object not found"};
16623         }
16624         
16625         if(o.metaData){
16626             
16627             delete this.ef;
16628             this.metaFromRemote = true;
16629             this.meta = o.metaData;
16630             this.recordType = Roo.data.Record.create(o.metaData.fields);
16631             this.onMetaChange(this.meta, this.recordType, o);
16632         }
16633         return this.readRecords(o);
16634     },
16635
16636     // private function a store will implement
16637     onMetaChange : function(meta, recordType, o){
16638
16639     },
16640
16641     /**
16642          * @ignore
16643          */
16644     simpleAccess: function(obj, subsc) {
16645         return obj[subsc];
16646     },
16647
16648         /**
16649          * @ignore
16650          */
16651     getJsonAccessor: function(){
16652         var re = /[\[\.]/;
16653         return function(expr) {
16654             try {
16655                 return(re.test(expr))
16656                     ? new Function("obj", "return obj." + expr)
16657                     : function(obj){
16658                         return obj[expr];
16659                     };
16660             } catch(e){}
16661             return Roo.emptyFn;
16662         };
16663     }(),
16664
16665     /**
16666      * Create a data block containing Roo.data.Records from an XML document.
16667      * @param {Object} o An object which contains an Array of row objects in the property specified
16668      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16669      * which contains the total size of the dataset.
16670      * @return {Object} data A data block which is used by an Roo.data.Store object as
16671      * a cache of Roo.data.Records.
16672      */
16673     readRecords : function(o){
16674         /**
16675          * After any data loads, the raw JSON data is available for further custom processing.
16676          * @type Object
16677          */
16678         this.o = o;
16679         var s = this.meta, Record = this.recordType,
16680             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16681
16682 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16683         if (!this.ef) {
16684             if(s.totalProperty) {
16685                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16686                 }
16687                 if(s.successProperty) {
16688                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16689                 }
16690                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16691                 if (s.id) {
16692                         var g = this.getJsonAccessor(s.id);
16693                         this.getId = function(rec) {
16694                                 var r = g(rec);  
16695                                 return (r === undefined || r === "") ? null : r;
16696                         };
16697                 } else {
16698                         this.getId = function(){return null;};
16699                 }
16700             this.ef = [];
16701             for(var jj = 0; jj < fl; jj++){
16702                 f = fi[jj];
16703                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16704                 this.ef[jj] = this.getJsonAccessor(map);
16705             }
16706         }
16707
16708         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16709         if(s.totalProperty){
16710             var vt = parseInt(this.getTotal(o), 10);
16711             if(!isNaN(vt)){
16712                 totalRecords = vt;
16713             }
16714         }
16715         if(s.successProperty){
16716             var vs = this.getSuccess(o);
16717             if(vs === false || vs === 'false'){
16718                 success = false;
16719             }
16720         }
16721         var records = [];
16722         for(var i = 0; i < c; i++){
16723             var n = root[i];
16724             var values = {};
16725             var id = this.getId(n);
16726             for(var j = 0; j < fl; j++){
16727                 f = fi[j];
16728                                 var v = this.ef[j](n);
16729                                 if (!f.convert) {
16730                                         Roo.log('missing convert for ' + f.name);
16731                                         Roo.log(f);
16732                                         continue;
16733                                 }
16734                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16735             }
16736                         if (!Record) {
16737                                 return {
16738                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16739                                         success : false,
16740                                         records : [],
16741                                         totalRecords : 0
16742                                 };
16743                         }
16744             var record = new Record(values, id);
16745             record.json = n;
16746             records[i] = record;
16747         }
16748         return {
16749             raw : o,
16750             success : success,
16751             records : records,
16752             totalRecords : totalRecords
16753         };
16754     },
16755     // used when loading children.. @see loadDataFromChildren
16756     toLoadData: function(rec)
16757     {
16758         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16759         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16760         return { data : data, total : data.length };
16761         
16762     }
16763 });/*
16764  * Based on:
16765  * Ext JS Library 1.1.1
16766  * Copyright(c) 2006-2007, Ext JS, LLC.
16767  *
16768  * Originally Released Under LGPL - original licence link has changed is not relivant.
16769  *
16770  * Fork - LGPL
16771  * <script type="text/javascript">
16772  */
16773
16774 /**
16775  * @class Roo.data.ArrayReader
16776  * @extends Roo.data.DataReader
16777  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16778  * Each element of that Array represents a row of data fields. The
16779  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16780  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16781  * <p>
16782  * Example code:.
16783  * <pre><code>
16784 var RecordDef = Roo.data.Record.create([
16785     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16786     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16787 ]);
16788 var myReader = new Roo.data.ArrayReader({
16789     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16790 }, RecordDef);
16791 </code></pre>
16792  * <p>
16793  * This would consume an Array like this:
16794  * <pre><code>
16795 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16796   </code></pre>
16797  
16798  * @constructor
16799  * Create a new JsonReader
16800  * @param {Object} meta Metadata configuration options.
16801  * @param {Object|Array} recordType Either an Array of field definition objects
16802  * 
16803  * @cfg {Array} fields Array of field definition objects
16804  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16805  * as specified to {@link Roo.data.Record#create},
16806  * or an {@link Roo.data.Record} object
16807  *
16808  * 
16809  * created using {@link Roo.data.Record#create}.
16810  */
16811 Roo.data.ArrayReader = function(meta, recordType)
16812 {    
16813     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16814 };
16815
16816 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16817     
16818       /**
16819      * Create a data block containing Roo.data.Records from an XML document.
16820      * @param {Object} o An Array of row objects which represents the dataset.
16821      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16822      * a cache of Roo.data.Records.
16823      */
16824     readRecords : function(o)
16825     {
16826         var sid = this.meta ? this.meta.id : null;
16827         var recordType = this.recordType, fields = recordType.prototype.fields;
16828         var records = [];
16829         var root = o;
16830         for(var i = 0; i < root.length; i++){
16831             var n = root[i];
16832             var values = {};
16833             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16834             for(var j = 0, jlen = fields.length; j < jlen; j++){
16835                 var f = fields.items[j];
16836                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16837                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16838                 v = f.convert(v);
16839                 values[f.name] = v;
16840             }
16841             var record = new recordType(values, id);
16842             record.json = n;
16843             records[records.length] = record;
16844         }
16845         return {
16846             records : records,
16847             totalRecords : records.length
16848         };
16849     },
16850     // used when loading children.. @see loadDataFromChildren
16851     toLoadData: function(rec)
16852     {
16853         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16854         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16855         
16856     }
16857     
16858     
16859 });/*
16860  * - LGPL
16861  * * 
16862  */
16863
16864 /**
16865  * @class Roo.bootstrap.form.ComboBox
16866  * @extends Roo.bootstrap.form.TriggerField
16867  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16868  * @cfg {Boolean} append (true|false) default false
16869  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16870  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16871  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16872  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16873  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16874  * @cfg {Boolean} animate default true
16875  * @cfg {Boolean} emptyResultText only for touch device
16876  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16877  * @cfg {String} emptyTitle default ''
16878  * @cfg {Number} width fixed with? experimental
16879  * @constructor
16880  * Create a new ComboBox.
16881  * @param {Object} config Configuration options
16882  */
16883 Roo.bootstrap.form.ComboBox = function(config){
16884     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16885     this.addEvents({
16886         /**
16887          * @event expand
16888          * Fires when the dropdown list is expanded
16889         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16890         */
16891         'expand' : true,
16892         /**
16893          * @event collapse
16894          * Fires when the dropdown list is collapsed
16895         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16896         */
16897         'collapse' : true,
16898         /**
16899          * @event beforeselect
16900          * Fires before a list item is selected. Return false to cancel the selection.
16901         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902         * @param {Roo.data.Record} record The data record returned from the underlying store
16903         * @param {Number} index The index of the selected item in the dropdown list
16904         */
16905         'beforeselect' : true,
16906         /**
16907          * @event select
16908          * Fires when a list item is selected
16909         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16910         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16911         * @param {Number} index The index of the selected item in the dropdown list
16912         */
16913         'select' : true,
16914         /**
16915          * @event beforequery
16916          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16917          * The event object passed has these properties:
16918         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16919         * @param {String} query The query
16920         * @param {Boolean} forceAll true to force "all" query
16921         * @param {Boolean} cancel true to cancel the query
16922         * @param {Object} e The query event object
16923         */
16924         'beforequery': true,
16925          /**
16926          * @event add
16927          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16928         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16929         */
16930         'add' : true,
16931         /**
16932          * @event edit
16933          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16934         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16935         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16936         */
16937         'edit' : true,
16938         /**
16939          * @event remove
16940          * Fires when the remove value from the combobox array
16941         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16942         */
16943         'remove' : true,
16944         /**
16945          * @event afterremove
16946          * Fires when the remove value from the combobox array
16947         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16948         */
16949         'afterremove' : true,
16950         /**
16951          * @event specialfilter
16952          * Fires when specialfilter
16953             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16954             */
16955         'specialfilter' : true,
16956         /**
16957          * @event tick
16958          * Fires when tick the element
16959             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16960             */
16961         'tick' : true,
16962         /**
16963          * @event touchviewdisplay
16964          * Fires when touch view require special display (default is using displayField)
16965             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16966             * @param {Object} cfg set html .
16967             */
16968         'touchviewdisplay' : true
16969         
16970     });
16971     
16972     this.item = [];
16973     this.tickItems = [];
16974     
16975     this.selectedIndex = -1;
16976     if(this.mode == 'local'){
16977         if(config.queryDelay === undefined){
16978             this.queryDelay = 10;
16979         }
16980         if(config.minChars === undefined){
16981             this.minChars = 0;
16982         }
16983     }
16984 };
16985
16986 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16987      
16988     /**
16989      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16990      * rendering into an Roo.Editor, defaults to false)
16991      */
16992     /**
16993      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16994      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16995      */
16996     /**
16997      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16998      */
16999     /**
17000      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
17001      * the dropdown list (defaults to undefined, with no header element)
17002      */
17003
17004      /**
17005      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
17006      */
17007      
17008      /**
17009      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
17010      */
17011     listWidth: undefined,
17012     /**
17013      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
17014      * mode = 'remote' or 'text' if mode = 'local')
17015      */
17016     displayField: undefined,
17017     
17018     /**
17019      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
17020      * mode = 'remote' or 'value' if mode = 'local'). 
17021      * Note: use of a valueField requires the user make a selection
17022      * in order for a value to be mapped.
17023      */
17024     valueField: undefined,
17025     /**
17026      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17027      */
17028     modalTitle : '',
17029     
17030     /**
17031      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17032      * field's data value (defaults to the underlying DOM element's name)
17033      */
17034     hiddenName: undefined,
17035     /**
17036      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17037      */
17038     listClass: '',
17039     /**
17040      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17041      */
17042     selectedClass: 'active',
17043     
17044     /**
17045      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17046      */
17047     shadow:'sides',
17048     /**
17049      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17050      * anchor positions (defaults to 'tl-bl')
17051      */
17052     listAlign: 'tl-bl?',
17053     /**
17054      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17055      */
17056     maxHeight: 300,
17057     /**
17058      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17059      * query specified by the allQuery config option (defaults to 'query')
17060      */
17061     triggerAction: 'query',
17062     /**
17063      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17064      * (defaults to 4, does not apply if editable = false)
17065      */
17066     minChars : 4,
17067     /**
17068      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17069      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17070      */
17071     typeAhead: false,
17072     /**
17073      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17074      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17075      */
17076     queryDelay: 500,
17077     /**
17078      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17079      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17080      */
17081     pageSize: 0,
17082     /**
17083      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17084      * when editable = true (defaults to false)
17085      */
17086     selectOnFocus:false,
17087     /**
17088      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17089      */
17090     queryParam: 'query',
17091     /**
17092      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17093      * when mode = 'remote' (defaults to 'Loading...')
17094      */
17095     loadingText: 'Loading...',
17096     /**
17097      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17098      */
17099     resizable: false,
17100     /**
17101      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17102      */
17103     handleHeight : 8,
17104     /**
17105      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17106      * traditional select (defaults to true)
17107      */
17108     editable: true,
17109     /**
17110      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17111      */
17112     allQuery: '',
17113     /**
17114      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17115      */
17116     mode: 'remote',
17117     /**
17118      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17119      * listWidth has a higher value)
17120      */
17121     minListWidth : 70,
17122     /**
17123      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17124      * allow the user to set arbitrary text into the field (defaults to false)
17125      */
17126     forceSelection:false,
17127     /**
17128      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17129      * if typeAhead = true (defaults to 250)
17130      */
17131     typeAheadDelay : 250,
17132     /**
17133      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17134      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17135      */
17136     valueNotFoundText : undefined,
17137     /**
17138      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17139      */
17140     blockFocus : false,
17141     
17142     /**
17143      * @cfg {Boolean} disableClear Disable showing of clear button.
17144      */
17145     disableClear : false,
17146     /**
17147      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17148      */
17149     alwaysQuery : false,
17150     
17151     /**
17152      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17153      */
17154     multiple : false,
17155     
17156     /**
17157      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17158      */
17159     invalidClass : "has-warning",
17160     
17161     /**
17162      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17163      */
17164     validClass : "has-success",
17165     
17166     /**
17167      * @cfg {Boolean} specialFilter (true|false) special filter default false
17168      */
17169     specialFilter : false,
17170     
17171     /**
17172      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17173      */
17174     mobileTouchView : true,
17175     
17176     /**
17177      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17178      */
17179     useNativeIOS : false,
17180     
17181     /**
17182      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17183      */
17184     mobile_restrict_height : false,
17185     
17186     ios_options : false,
17187     
17188     //private
17189     addicon : false,
17190     editicon: false,
17191     
17192     page: 0,
17193     hasQuery: false,
17194     append: false,
17195     loadNext: false,
17196     autoFocus : true,
17197     tickable : false,
17198     btnPosition : 'right',
17199     triggerList : true,
17200     showToggleBtn : true,
17201     animate : true,
17202     emptyResultText: 'Empty',
17203     triggerText : 'Select',
17204     emptyTitle : '',
17205     width : false,
17206     
17207     // element that contains real text value.. (when hidden is used..)
17208     
17209     getAutoCreate : function()
17210     {   
17211         var cfg = false;
17212         //render
17213         /*
17214          * Render classic select for iso
17215          */
17216         
17217         if(Roo.isIOS && this.useNativeIOS){
17218             cfg = this.getAutoCreateNativeIOS();
17219             return cfg;
17220         }
17221         
17222         /*
17223          * Touch Devices
17224          */
17225         
17226         if(Roo.isTouch && this.mobileTouchView){
17227             cfg = this.getAutoCreateTouchView();
17228             return cfg;;
17229         }
17230         
17231         /*
17232          *  Normal ComboBox
17233          */
17234         if(!this.tickable){
17235             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17236             return cfg;
17237         }
17238         
17239         /*
17240          *  ComboBox with tickable selections
17241          */
17242              
17243         var align = this.labelAlign || this.parentLabelAlign();
17244         
17245         cfg = {
17246             cls : 'form-group roo-combobox-tickable' //input-group
17247         };
17248         
17249         var btn_text_select = '';
17250         var btn_text_done = '';
17251         var btn_text_cancel = '';
17252         
17253         if (this.btn_text_show) {
17254             btn_text_select = 'Select';
17255             btn_text_done = 'Done';
17256             btn_text_cancel = 'Cancel'; 
17257         }
17258         
17259         var buttons = {
17260             tag : 'div',
17261             cls : 'tickable-buttons',
17262             cn : [
17263                 {
17264                     tag : 'button',
17265                     type : 'button',
17266                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17267                     //html : this.triggerText
17268                     html: btn_text_select
17269                 },
17270                 {
17271                     tag : 'button',
17272                     type : 'button',
17273                     name : 'ok',
17274                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17275                     //html : 'Done'
17276                     html: btn_text_done
17277                 },
17278                 {
17279                     tag : 'button',
17280                     type : 'button',
17281                     name : 'cancel',
17282                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17283                     //html : 'Cancel'
17284                     html: btn_text_cancel
17285                 }
17286             ]
17287         };
17288         
17289         if(this.editable){
17290             buttons.cn.unshift({
17291                 tag: 'input',
17292                 cls: 'roo-select2-search-field-input'
17293             });
17294         }
17295         
17296         var _this = this;
17297         
17298         Roo.each(buttons.cn, function(c){
17299             if (_this.size) {
17300                 c.cls += ' btn-' + _this.size;
17301             }
17302
17303             if (_this.disabled) {
17304                 c.disabled = true;
17305             }
17306         });
17307         
17308         var box = {
17309             tag: 'div',
17310             style : 'display: contents',
17311             cn: [
17312                 {
17313                     tag: 'input',
17314                     type : 'hidden',
17315                     cls: 'form-hidden-field'
17316                 },
17317                 {
17318                     tag: 'ul',
17319                     cls: 'roo-select2-choices',
17320                     cn:[
17321                         {
17322                             tag: 'li',
17323                             cls: 'roo-select2-search-field',
17324                             cn: [
17325                                 buttons
17326                             ]
17327                         }
17328                     ]
17329                 }
17330             ]
17331         };
17332         
17333         var combobox = {
17334             cls: 'roo-select2-container input-group roo-select2-container-multi',
17335             cn: [
17336                 
17337                 box
17338 //                {
17339 //                    tag: 'ul',
17340 //                    cls: 'typeahead typeahead-long dropdown-menu',
17341 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17342 //                }
17343             ]
17344         };
17345         
17346         if(this.hasFeedback && !this.allowBlank){
17347             
17348             var feedback = {
17349                 tag: 'span',
17350                 cls: 'glyphicon form-control-feedback'
17351             };
17352
17353             combobox.cn.push(feedback);
17354         }
17355         
17356         
17357         
17358         var indicator = {
17359             tag : 'i',
17360             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17361             tooltip : 'This field is required'
17362         };
17363         if (Roo.bootstrap.version == 4) {
17364             indicator = {
17365                 tag : 'i',
17366                 style : 'display:none'
17367             };
17368         }
17369         if (align ==='left' && this.fieldLabel.length) {
17370             
17371             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17372             
17373             cfg.cn = [
17374                 indicator,
17375                 {
17376                     tag: 'label',
17377                     'for' :  id,
17378                     cls : 'control-label col-form-label',
17379                     html : this.fieldLabel
17380
17381                 },
17382                 {
17383                     cls : "", 
17384                     cn: [
17385                         combobox
17386                     ]
17387                 }
17388
17389             ];
17390             
17391             var labelCfg = cfg.cn[1];
17392             var contentCfg = cfg.cn[2];
17393             
17394
17395             if(this.indicatorpos == 'right'){
17396                 
17397                 cfg.cn = [
17398                     {
17399                         tag: 'label',
17400                         'for' :  id,
17401                         cls : 'control-label col-form-label',
17402                         cn : [
17403                             {
17404                                 tag : 'span',
17405                                 html : this.fieldLabel
17406                             },
17407                             indicator
17408                         ]
17409                     },
17410                     {
17411                         cls : "",
17412                         cn: [
17413                             combobox
17414                         ]
17415                     }
17416
17417                 ];
17418                 
17419                 
17420                 
17421                 labelCfg = cfg.cn[0];
17422                 contentCfg = cfg.cn[1];
17423             
17424             }
17425             
17426             if(this.labelWidth > 12){
17427                 labelCfg.style = "width: " + this.labelWidth + 'px';
17428             }
17429             if(this.width * 1 > 0){
17430                 contentCfg.style = "width: " + this.width + 'px';
17431             }
17432             if(this.labelWidth < 13 && this.labelmd == 0){
17433                 this.labelmd = this.labelWidth;
17434             }
17435             
17436             if(this.labellg > 0){
17437                 labelCfg.cls += ' col-lg-' + this.labellg;
17438                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17439             }
17440             
17441             if(this.labelmd > 0){
17442                 labelCfg.cls += ' col-md-' + this.labelmd;
17443                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17444             }
17445             
17446             if(this.labelsm > 0){
17447                 labelCfg.cls += ' col-sm-' + this.labelsm;
17448                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17449             }
17450             
17451             if(this.labelxs > 0){
17452                 labelCfg.cls += ' col-xs-' + this.labelxs;
17453                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17454             }
17455                 
17456                 
17457         } else if ( this.fieldLabel.length) {
17458 //                Roo.log(" label");
17459                  cfg.cn = [
17460                    indicator,
17461                     {
17462                         tag: 'label',
17463                         //cls : 'input-group-addon',
17464                         html : this.fieldLabel
17465                     },
17466                     combobox
17467                 ];
17468                 
17469                 if(this.indicatorpos == 'right'){
17470                     cfg.cn = [
17471                         {
17472                             tag: 'label',
17473                             //cls : 'input-group-addon',
17474                             html : this.fieldLabel
17475                         },
17476                         indicator,
17477                         combobox
17478                     ];
17479                     
17480                 }
17481
17482         } else {
17483             
17484 //                Roo.log(" no label && no align");
17485                 cfg = combobox
17486                      
17487                 
17488         }
17489          
17490         var settings=this;
17491         ['xs','sm','md','lg'].map(function(size){
17492             if (settings[size]) {
17493                 cfg.cls += ' col-' + size + '-' + settings[size];
17494             }
17495         });
17496         
17497         return cfg;
17498         
17499     },
17500     
17501     _initEventsCalled : false,
17502     
17503     // private
17504     initEvents: function()
17505     {   
17506         if (this._initEventsCalled) { // as we call render... prevent looping...
17507             return;
17508         }
17509         this._initEventsCalled = true;
17510         
17511         if (!this.store) {
17512             throw "can not find store for combo";
17513         }
17514         
17515         this.indicator = this.indicatorEl();
17516         
17517         this.store = Roo.factory(this.store, Roo.data);
17518         this.store.parent = this;
17519         
17520         // if we are building from html. then this element is so complex, that we can not really
17521         // use the rendered HTML.
17522         // so we have to trash and replace the previous code.
17523         if (Roo.XComponent.build_from_html) {
17524             // remove this element....
17525             var e = this.el.dom, k=0;
17526             while (e ) { e = e.previousSibling;  ++k;}
17527
17528             this.el.remove();
17529             
17530             this.el=false;
17531             this.rendered = false;
17532             
17533             this.render(this.parent().getChildContainer(true), k);
17534         }
17535         
17536         if(Roo.isIOS && this.useNativeIOS){
17537             this.initIOSView();
17538             return;
17539         }
17540         
17541         /*
17542          * Touch Devices
17543          */
17544         
17545         if(Roo.isTouch && this.mobileTouchView){
17546             this.initTouchView();
17547             return;
17548         }
17549         
17550         if(this.tickable){
17551             this.initTickableEvents();
17552             return;
17553         }
17554         
17555         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17556         
17557         if(this.hiddenName){
17558             
17559             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17560             
17561             this.hiddenField.dom.value =
17562                 this.hiddenValue !== undefined ? this.hiddenValue :
17563                 this.value !== undefined ? this.value : '';
17564
17565             // prevent input submission
17566             this.el.dom.removeAttribute('name');
17567             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17568              
17569              
17570         }
17571         //if(Roo.isGecko){
17572         //    this.el.dom.setAttribute('autocomplete', 'off');
17573         //}
17574         
17575         var cls = 'x-combo-list';
17576         
17577         //this.list = new Roo.Layer({
17578         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17579         //});
17580         
17581         var _this = this;
17582         
17583         (function(){
17584             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17585             _this.list.setWidth(lw);
17586         }).defer(100);
17587         
17588         this.list.on('mouseover', this.onViewOver, this);
17589         this.list.on('mousemove', this.onViewMove, this);
17590         this.list.on('scroll', this.onViewScroll, this);
17591         
17592         /*
17593         this.list.swallowEvent('mousewheel');
17594         this.assetHeight = 0;
17595
17596         if(this.title){
17597             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17598             this.assetHeight += this.header.getHeight();
17599         }
17600
17601         this.innerList = this.list.createChild({cls:cls+'-inner'});
17602         this.innerList.on('mouseover', this.onViewOver, this);
17603         this.innerList.on('mousemove', this.onViewMove, this);
17604         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17605         
17606         if(this.allowBlank && !this.pageSize && !this.disableClear){
17607             this.footer = this.list.createChild({cls:cls+'-ft'});
17608             this.pageTb = new Roo.Toolbar(this.footer);
17609            
17610         }
17611         if(this.pageSize){
17612             this.footer = this.list.createChild({cls:cls+'-ft'});
17613             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17614                     {pageSize: this.pageSize});
17615             
17616         }
17617         
17618         if (this.pageTb && this.allowBlank && !this.disableClear) {
17619             var _this = this;
17620             this.pageTb.add(new Roo.Toolbar.Fill(), {
17621                 cls: 'x-btn-icon x-btn-clear',
17622                 text: '&#160;',
17623                 handler: function()
17624                 {
17625                     _this.collapse();
17626                     _this.clearValue();
17627                     _this.onSelect(false, -1);
17628                 }
17629             });
17630         }
17631         if (this.footer) {
17632             this.assetHeight += this.footer.getHeight();
17633         }
17634         */
17635             
17636         if(!this.tpl){
17637             this.tpl = Roo.bootstrap.version == 4 ?
17638                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17639                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17640         }
17641
17642         this.view = new Roo.View(this.list, this.tpl, {
17643             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17644         });
17645         //this.view.wrapEl.setDisplayed(false);
17646         this.view.on('click', this.onViewClick, this);
17647         
17648         
17649         this.store.on('beforeload', this.onBeforeLoad, this);
17650         this.store.on('load', this.onLoad, this);
17651         this.store.on('loadexception', this.onLoadException, this);
17652         /*
17653         if(this.resizable){
17654             this.resizer = new Roo.Resizable(this.list,  {
17655                pinned:true, handles:'se'
17656             });
17657             this.resizer.on('resize', function(r, w, h){
17658                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17659                 this.listWidth = w;
17660                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17661                 this.restrictHeight();
17662             }, this);
17663             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17664         }
17665         */
17666         if(!this.editable){
17667             this.editable = true;
17668             this.setEditable(false);
17669         }
17670         
17671         /*
17672         
17673         if (typeof(this.events.add.listeners) != 'undefined') {
17674             
17675             this.addicon = this.wrap.createChild(
17676                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17677        
17678             this.addicon.on('click', function(e) {
17679                 this.fireEvent('add', this);
17680             }, this);
17681         }
17682         if (typeof(this.events.edit.listeners) != 'undefined') {
17683             
17684             this.editicon = this.wrap.createChild(
17685                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17686             if (this.addicon) {
17687                 this.editicon.setStyle('margin-left', '40px');
17688             }
17689             this.editicon.on('click', function(e) {
17690                 
17691                 // we fire even  if inothing is selected..
17692                 this.fireEvent('edit', this, this.lastData );
17693                 
17694             }, this);
17695         }
17696         */
17697         
17698         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17699             "up" : function(e){
17700                 this.inKeyMode = true;
17701                 this.selectPrev();
17702             },
17703
17704             "down" : function(e){
17705                 if(!this.isExpanded()){
17706                     this.onTriggerClick();
17707                 }else{
17708                     this.inKeyMode = true;
17709                     this.selectNext();
17710                 }
17711             },
17712
17713             "enter" : function(e){
17714 //                this.onViewClick();
17715                 //return true;
17716                 this.collapse();
17717                 
17718                 if(this.fireEvent("specialkey", this, e)){
17719                     this.onViewClick(false);
17720                 }
17721                 
17722                 return true;
17723             },
17724
17725             "esc" : function(e){
17726                 this.collapse();
17727             },
17728
17729             "tab" : function(e){
17730                 this.collapse();
17731                 
17732                 if(this.fireEvent("specialkey", this, e)){
17733                     this.onViewClick(false);
17734                 }
17735                 
17736                 return true;
17737             },
17738
17739             scope : this,
17740
17741             doRelay : function(foo, bar, hname){
17742                 if(hname == 'down' || this.scope.isExpanded()){
17743                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17744                 }
17745                 return true;
17746             },
17747
17748             forceKeyDown: true
17749         });
17750         
17751         
17752         this.queryDelay = Math.max(this.queryDelay || 10,
17753                 this.mode == 'local' ? 10 : 250);
17754         
17755         
17756         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17757         
17758         if(this.typeAhead){
17759             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17760         }
17761         if(this.editable !== false){
17762             this.inputEl().on("keyup", this.onKeyUp, this);
17763         }
17764         if(this.forceSelection){
17765             this.inputEl().on('blur', this.doForce, this);
17766         }
17767         
17768         if(this.multiple){
17769             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17770             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17771         }
17772     },
17773     
17774     initTickableEvents: function()
17775     {   
17776         this.createList();
17777         
17778         if(this.hiddenName){
17779             
17780             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17781             
17782             this.hiddenField.dom.value =
17783                 this.hiddenValue !== undefined ? this.hiddenValue :
17784                 this.value !== undefined ? this.value : '';
17785
17786             // prevent input submission
17787             this.el.dom.removeAttribute('name');
17788             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17789              
17790              
17791         }
17792         
17793 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17794         
17795         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17796         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17797         if(this.triggerList){
17798             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17799         }
17800          
17801         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17802         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17803         
17804         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17805         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17806         
17807         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17808         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17809         
17810         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17811         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17812         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17813         
17814         this.okBtn.hide();
17815         this.cancelBtn.hide();
17816         
17817         var _this = this;
17818         
17819         (function(){
17820             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17821             _this.list.setWidth(lw);
17822         }).defer(100);
17823         
17824         this.list.on('mouseover', this.onViewOver, this);
17825         this.list.on('mousemove', this.onViewMove, this);
17826         
17827         this.list.on('scroll', this.onViewScroll, this);
17828         
17829         if(!this.tpl){
17830             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17831                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17832         }
17833
17834         this.view = new Roo.View(this.list, this.tpl, {
17835             singleSelect:true,
17836             tickable:true,
17837             parent:this,
17838             store: this.store,
17839             selectedClass: this.selectedClass
17840         });
17841         
17842         //this.view.wrapEl.setDisplayed(false);
17843         this.view.on('click', this.onViewClick, this);
17844         
17845         
17846         
17847         this.store.on('beforeload', this.onBeforeLoad, this);
17848         this.store.on('load', this.onLoad, this);
17849         this.store.on('loadexception', this.onLoadException, this);
17850         
17851         if(this.editable){
17852             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17853                 "up" : function(e){
17854                     this.inKeyMode = true;
17855                     this.selectPrev();
17856                 },
17857
17858                 "down" : function(e){
17859                     this.inKeyMode = true;
17860                     this.selectNext();
17861                 },
17862
17863                 "enter" : function(e){
17864                     if(this.fireEvent("specialkey", this, e)){
17865                         this.onViewClick(false);
17866                     }
17867                     
17868                     return true;
17869                 },
17870
17871                 "esc" : function(e){
17872                     this.onTickableFooterButtonClick(e, false, false);
17873                 },
17874
17875                 "tab" : function(e){
17876                     this.fireEvent("specialkey", this, e);
17877                     
17878                     this.onTickableFooterButtonClick(e, false, false);
17879                     
17880                     return true;
17881                 },
17882
17883                 scope : this,
17884
17885                 doRelay : function(e, fn, key){
17886                     if(this.scope.isExpanded()){
17887                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17888                     }
17889                     return true;
17890                 },
17891
17892                 forceKeyDown: true
17893             });
17894         }
17895         
17896         this.queryDelay = Math.max(this.queryDelay || 10,
17897                 this.mode == 'local' ? 10 : 250);
17898         
17899         
17900         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17901         
17902         if(this.typeAhead){
17903             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17904         }
17905         
17906         if(this.editable !== false){
17907             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17908         }
17909         
17910         this.indicator = this.indicatorEl();
17911         
17912         if(this.indicator){
17913             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17914             this.indicator.hide();
17915         }
17916         
17917     },
17918
17919     onDestroy : function(){
17920         if(this.view){
17921             this.view.setStore(null);
17922             this.view.el.removeAllListeners();
17923             this.view.el.remove();
17924             this.view.purgeListeners();
17925         }
17926         if(this.list){
17927             this.list.dom.innerHTML  = '';
17928         }
17929         
17930         if(this.store){
17931             this.store.un('beforeload', this.onBeforeLoad, this);
17932             this.store.un('load', this.onLoad, this);
17933             this.store.un('loadexception', this.onLoadException, this);
17934         }
17935         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17936     },
17937
17938     // private
17939     fireKey : function(e){
17940         if(e.isNavKeyPress() && !this.list.isVisible()){
17941             this.fireEvent("specialkey", this, e);
17942         }
17943     },
17944
17945     // private
17946     onResize: function(w, h)
17947     {
17948         
17949         
17950 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17951 //        
17952 //        if(typeof w != 'number'){
17953 //            // we do not handle it!?!?
17954 //            return;
17955 //        }
17956 //        var tw = this.trigger.getWidth();
17957 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17958 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17959 //        var x = w - tw;
17960 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17961 //            
17962 //        //this.trigger.setStyle('left', x+'px');
17963 //        
17964 //        if(this.list && this.listWidth === undefined){
17965 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17966 //            this.list.setWidth(lw);
17967 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17968 //        }
17969         
17970     
17971         
17972     },
17973
17974     /**
17975      * Allow or prevent the user from directly editing the field text.  If false is passed,
17976      * the user will only be able to select from the items defined in the dropdown list.  This method
17977      * is the runtime equivalent of setting the 'editable' config option at config time.
17978      * @param {Boolean} value True to allow the user to directly edit the field text
17979      */
17980     setEditable : function(value){
17981         if(value == this.editable){
17982             return;
17983         }
17984         this.editable = value;
17985         if(!value){
17986             this.inputEl().dom.setAttribute('readOnly', true);
17987             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17988             this.inputEl().addClass('x-combo-noedit');
17989         }else{
17990             this.inputEl().dom.removeAttribute('readOnly');
17991             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17992             this.inputEl().removeClass('x-combo-noedit');
17993         }
17994     },
17995
17996     // private
17997     
17998     onBeforeLoad : function(combo,opts){
17999         if(!this.hasFocus){
18000             return;
18001         }
18002          if (!opts.add) {
18003             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
18004          }
18005         this.restrictHeight();
18006         this.selectedIndex = -1;
18007     },
18008
18009     // private
18010     onLoad : function(){
18011         
18012         this.hasQuery = false;
18013         
18014         if(!this.hasFocus){
18015             return;
18016         }
18017         
18018         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18019             this.loading.hide();
18020         }
18021         
18022         if(this.store.getCount() > 0){
18023             
18024             this.expand();
18025             this.restrictHeight();
18026             if(this.lastQuery == this.allQuery){
18027                 if(this.editable && !this.tickable){
18028                     this.inputEl().dom.select();
18029                 }
18030                 
18031                 if(
18032                     !this.selectByValue(this.value, true) &&
18033                     this.autoFocus && 
18034                     (
18035                         !this.store.lastOptions ||
18036                         typeof(this.store.lastOptions.add) == 'undefined' || 
18037                         this.store.lastOptions.add != true
18038                     )
18039                 ){
18040                     this.select(0, true);
18041                 }
18042             }else{
18043                 if(this.autoFocus){
18044                     this.selectNext();
18045                 }
18046                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18047                     this.taTask.delay(this.typeAheadDelay);
18048                 }
18049             }
18050         }else{
18051             this.onEmptyResults();
18052         }
18053         
18054         //this.el.focus();
18055     },
18056     // private
18057     onLoadException : function()
18058     {
18059         this.hasQuery = false;
18060         
18061         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18062             this.loading.hide();
18063         }
18064         
18065         if(this.tickable && this.editable){
18066             return;
18067         }
18068         
18069         this.collapse();
18070         // only causes errors at present
18071         //Roo.log(this.store.reader.jsonData);
18072         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18073             // fixme
18074             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18075         //}
18076         
18077         
18078     },
18079     // private
18080     onTypeAhead : function(){
18081         if(this.store.getCount() > 0){
18082             var r = this.store.getAt(0);
18083             var newValue = r.data[this.displayField];
18084             var len = newValue.length;
18085             var selStart = this.getRawValue().length;
18086             
18087             if(selStart != len){
18088                 this.setRawValue(newValue);
18089                 this.selectText(selStart, newValue.length);
18090             }
18091         }
18092     },
18093
18094     // private
18095     onSelect : function(record, index){
18096         
18097         if(this.fireEvent('beforeselect', this, record, index) !== false){
18098         
18099             this.setFromData(index > -1 ? record.data : false);
18100             
18101             this.collapse();
18102             this.fireEvent('select', this, record, index);
18103         }
18104     },
18105
18106     /**
18107      * Returns the currently selected field value or empty string if no value is set.
18108      * @return {String} value The selected value
18109      */
18110     getValue : function()
18111     {
18112         if(Roo.isIOS && this.useNativeIOS){
18113             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18114         }
18115         
18116         if(this.multiple){
18117             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18118         }
18119         
18120         if(this.valueField){
18121             return typeof this.value != 'undefined' ? this.value : '';
18122         }else{
18123             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18124         }
18125     },
18126     
18127     getRawValue : function()
18128     {
18129         if(Roo.isIOS && this.useNativeIOS){
18130             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18131         }
18132         
18133         var v = this.inputEl().getValue();
18134         
18135         return v;
18136     },
18137
18138     /**
18139      * Clears any text/value currently set in the field
18140      */
18141     clearValue : function(){
18142         
18143         if(this.hiddenField){
18144             this.hiddenField.dom.value = '';
18145         }
18146         this.value = '';
18147         this.setRawValue('');
18148         this.lastSelectionText = '';
18149         this.lastData = false;
18150         
18151         var close = this.closeTriggerEl();
18152         
18153         if(close){
18154             close.hide();
18155         }
18156         
18157         this.validate();
18158         
18159     },
18160
18161     /**
18162      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18163      * will be displayed in the field.  If the value does not match the data value of an existing item,
18164      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18165      * Otherwise the field will be blank (although the value will still be set).
18166      * @param {String} value The value to match
18167      */
18168     setValue : function(v)
18169     {
18170         if(Roo.isIOS && this.useNativeIOS){
18171             this.setIOSValue(v);
18172             return;
18173         }
18174         
18175         if(this.multiple){
18176             this.syncValue();
18177             return;
18178         }
18179         
18180         var text = v;
18181         if(this.valueField){
18182             var r = this.findRecord(this.valueField, v);
18183             if(r){
18184                 text = r.data[this.displayField];
18185             }else if(this.valueNotFoundText !== undefined){
18186                 text = this.valueNotFoundText;
18187             }
18188         }
18189         this.lastSelectionText = text;
18190         if(this.hiddenField){
18191             this.hiddenField.dom.value = v;
18192         }
18193         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18194         this.value = v;
18195         
18196         var close = this.closeTriggerEl();
18197         
18198         if(close){
18199             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18200         }
18201         
18202         this.validate();
18203     },
18204     /**
18205      * @property {Object} the last set data for the element
18206      */
18207     
18208     lastData : false,
18209     /**
18210      * Sets the value of the field based on a object which is related to the record format for the store.
18211      * @param {Object} value the value to set as. or false on reset?
18212      */
18213     setFromData : function(o){
18214         
18215         if(this.multiple){
18216             this.addItem(o);
18217             return;
18218         }
18219             
18220         var dv = ''; // display value
18221         var vv = ''; // value value..
18222         this.lastData = o;
18223         if (this.displayField) {
18224             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18225         } else {
18226             // this is an error condition!!!
18227             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18228         }
18229         
18230         if(this.valueField){
18231             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18232         }
18233         
18234         var close = this.closeTriggerEl();
18235         
18236         if(close){
18237             if(dv.length || vv * 1 > 0){
18238                 close.show() ;
18239                 this.blockFocus=true;
18240             } else {
18241                 close.hide();
18242             }             
18243         }
18244         
18245         if(this.hiddenField){
18246             this.hiddenField.dom.value = vv;
18247             
18248             this.lastSelectionText = dv;
18249             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18250             this.value = vv;
18251             return;
18252         }
18253         // no hidden field.. - we store the value in 'value', but still display
18254         // display field!!!!
18255         this.lastSelectionText = dv;
18256         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18257         this.value = vv;
18258         
18259         
18260         
18261     },
18262     // private
18263     reset : function(){
18264         // overridden so that last data is reset..
18265         
18266         if(this.multiple){
18267             this.clearItem();
18268             return;
18269         }
18270         
18271         this.setValue(this.originalValue);
18272         //this.clearInvalid();
18273         this.lastData = false;
18274         if (this.view) {
18275             this.view.clearSelections();
18276         }
18277         
18278         this.validate();
18279     },
18280     // private
18281     findRecord : function(prop, value){
18282         var record;
18283         if(this.store.getCount() > 0){
18284             this.store.each(function(r){
18285                 if(r.data[prop] == value){
18286                     record = r;
18287                     return false;
18288                 }
18289                 return true;
18290             });
18291         }
18292         return record;
18293     },
18294     
18295     getName: function()
18296     {
18297         // returns hidden if it's set..
18298         if (!this.rendered) {return ''};
18299         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18300         
18301     },
18302     // private
18303     onViewMove : function(e, t){
18304         this.inKeyMode = false;
18305     },
18306
18307     // private
18308     onViewOver : function(e, t){
18309         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18310             return;
18311         }
18312         var item = this.view.findItemFromChild(t);
18313         
18314         if(item){
18315             var index = this.view.indexOf(item);
18316             this.select(index, false);
18317         }
18318     },
18319
18320     // private
18321     onViewClick : function(view, doFocus, el, e)
18322     {
18323         var index = this.view.getSelectedIndexes()[0];
18324         
18325         var r = this.store.getAt(index);
18326         
18327         if(this.tickable){
18328             
18329             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18330                 return;
18331             }
18332             
18333             var rm = false;
18334             var _this = this;
18335             
18336             Roo.each(this.tickItems, function(v,k){
18337                 
18338                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18339                     Roo.log(v);
18340                     _this.tickItems.splice(k, 1);
18341                     
18342                     if(typeof(e) == 'undefined' && view == false){
18343                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18344                     }
18345                     
18346                     rm = true;
18347                     return;
18348                 }
18349             });
18350             
18351             if(rm){
18352                 return;
18353             }
18354             
18355             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18356                 this.tickItems.push(r.data);
18357             }
18358             
18359             if(typeof(e) == 'undefined' && view == false){
18360                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18361             }
18362                     
18363             return;
18364         }
18365         
18366         if(r){
18367             this.onSelect(r, index);
18368         }
18369         if(doFocus !== false && !this.blockFocus){
18370             this.inputEl().focus();
18371         }
18372     },
18373
18374     // private
18375     restrictHeight : function(){
18376         //this.innerList.dom.style.height = '';
18377         //var inner = this.innerList.dom;
18378         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18379         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18380         //this.list.beginUpdate();
18381         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18382         this.list.alignTo(this.inputEl(), this.listAlign);
18383         this.list.alignTo(this.inputEl(), this.listAlign);
18384         //this.list.endUpdate();
18385     },
18386
18387     // private
18388     onEmptyResults : function(){
18389         
18390         if(this.tickable && this.editable){
18391             this.hasFocus = false;
18392             this.restrictHeight();
18393             return;
18394         }
18395         
18396         this.collapse();
18397     },
18398
18399     /**
18400      * Returns true if the dropdown list is expanded, else false.
18401      */
18402     isExpanded : function(){
18403         return this.list.isVisible();
18404     },
18405
18406     /**
18407      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18408      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18409      * @param {String} value The data value of the item to select
18410      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18411      * selected item if it is not currently in view (defaults to true)
18412      * @return {Boolean} True if the value matched an item in the list, else false
18413      */
18414     selectByValue : function(v, scrollIntoView){
18415         if(v !== undefined && v !== null){
18416             var r = this.findRecord(this.valueField || this.displayField, v);
18417             if(r){
18418                 this.select(this.store.indexOf(r), scrollIntoView);
18419                 return true;
18420             }
18421         }
18422         return false;
18423     },
18424
18425     /**
18426      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18427      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18428      * @param {Number} index The zero-based index of the list item to select
18429      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18430      * selected item if it is not currently in view (defaults to true)
18431      */
18432     select : function(index, scrollIntoView){
18433         this.selectedIndex = index;
18434         this.view.select(index);
18435         if(scrollIntoView !== false){
18436             var el = this.view.getNode(index);
18437             /*
18438              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18439              */
18440             if(el){
18441                 this.list.scrollChildIntoView(el, false);
18442             }
18443         }
18444     },
18445
18446     // private
18447     selectNext : function(){
18448         var ct = this.store.getCount();
18449         if(ct > 0){
18450             if(this.selectedIndex == -1){
18451                 this.select(0);
18452             }else if(this.selectedIndex < ct-1){
18453                 this.select(this.selectedIndex+1);
18454             }
18455         }
18456     },
18457
18458     // private
18459     selectPrev : function(){
18460         var ct = this.store.getCount();
18461         if(ct > 0){
18462             if(this.selectedIndex == -1){
18463                 this.select(0);
18464             }else if(this.selectedIndex != 0){
18465                 this.select(this.selectedIndex-1);
18466             }
18467         }
18468     },
18469
18470     // private
18471     onKeyUp : function(e){
18472         if(this.editable !== false && !e.isSpecialKey()){
18473             this.lastKey = e.getKey();
18474             this.dqTask.delay(this.queryDelay);
18475         }
18476     },
18477
18478     // private
18479     validateBlur : function(){
18480         return !this.list || !this.list.isVisible();   
18481     },
18482
18483     // private
18484     initQuery : function(){
18485         
18486         var v = this.getRawValue();
18487         
18488         if(this.tickable && this.editable){
18489             v = this.tickableInputEl().getValue();
18490         }
18491         
18492         this.doQuery(v);
18493     },
18494
18495     // private
18496     doForce : function(){
18497         if(this.inputEl().dom.value.length > 0){
18498             this.inputEl().dom.value =
18499                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18500              
18501         }
18502     },
18503
18504     /**
18505      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18506      * query allowing the query action to be canceled if needed.
18507      * @param {String} query The SQL query to execute
18508      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18509      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18510      * saved in the current store (defaults to false)
18511      */
18512     doQuery : function(q, forceAll){
18513         
18514         if(q === undefined || q === null){
18515             q = '';
18516         }
18517         var qe = {
18518             query: q,
18519             forceAll: forceAll,
18520             combo: this,
18521             cancel:false
18522         };
18523         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18524             return false;
18525         }
18526         q = qe.query;
18527         
18528         forceAll = qe.forceAll;
18529         if(forceAll === true || (q.length >= this.minChars)){
18530             
18531             this.hasQuery = true;
18532             
18533             if(this.lastQuery != q || this.alwaysQuery){
18534                 this.lastQuery = q;
18535                 if(this.mode == 'local'){
18536                     this.selectedIndex = -1;
18537                     if(forceAll){
18538                         this.store.clearFilter();
18539                     }else{
18540                         
18541                         if(this.specialFilter){
18542                             this.fireEvent('specialfilter', this);
18543                             this.onLoad();
18544                             return;
18545                         }
18546                         
18547                         this.store.filter(this.displayField, q);
18548                     }
18549                     
18550                     this.store.fireEvent("datachanged", this.store);
18551                     
18552                     this.onLoad();
18553                     
18554                     
18555                 }else{
18556                     
18557                     this.store.baseParams[this.queryParam] = q;
18558                     
18559                     var options = {params : this.getParams(q)};
18560                     
18561                     if(this.loadNext){
18562                         options.add = true;
18563                         options.params.start = this.page * this.pageSize;
18564                     }
18565                     
18566                     this.store.load(options);
18567                     
18568                     /*
18569                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18570                      *  we should expand the list on onLoad
18571                      *  so command out it
18572                      */
18573 //                    this.expand();
18574                 }
18575             }else{
18576                 this.selectedIndex = -1;
18577                 this.onLoad();   
18578             }
18579         }
18580         
18581         this.loadNext = false;
18582     },
18583     
18584     // private
18585     getParams : function(q){
18586         var p = {};
18587         //p[this.queryParam] = q;
18588         
18589         if(this.pageSize){
18590             p.start = 0;
18591             p.limit = this.pageSize;
18592         }
18593         return p;
18594     },
18595
18596     /**
18597      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18598      */
18599     collapse : function(){
18600         if(!this.isExpanded()){
18601             return;
18602         }
18603         
18604         this.list.hide();
18605         
18606         this.hasFocus = false;
18607         
18608         if(this.tickable){
18609             this.okBtn.hide();
18610             this.cancelBtn.hide();
18611             this.trigger.show();
18612             
18613             if(this.editable){
18614                 this.tickableInputEl().dom.value = '';
18615                 this.tickableInputEl().blur();
18616             }
18617             
18618         }
18619         
18620         Roo.get(document).un('mousedown', this.collapseIf, this);
18621         Roo.get(document).un('mousewheel', this.collapseIf, this);
18622         if (!this.editable) {
18623             Roo.get(document).un('keydown', this.listKeyPress, this);
18624         }
18625         this.fireEvent('collapse', this);
18626         
18627         this.validate();
18628     },
18629
18630     // private
18631     collapseIf : function(e){
18632         var in_combo  = e.within(this.el);
18633         var in_list =  e.within(this.list);
18634         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18635         
18636         if (in_combo || in_list || is_list) {
18637             //e.stopPropagation();
18638             return;
18639         }
18640         
18641         if(this.tickable){
18642             this.onTickableFooterButtonClick(e, false, false);
18643         }
18644
18645         this.collapse();
18646         
18647     },
18648
18649     /**
18650      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18651      */
18652     expand : function(){
18653        
18654         if(this.isExpanded() || !this.hasFocus){
18655             return;
18656         }
18657         
18658         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18659         this.list.setWidth(lw);
18660         
18661         Roo.log('expand');
18662         
18663         this.list.show();
18664         
18665         this.restrictHeight();
18666         
18667         if(this.tickable){
18668             
18669             this.tickItems = Roo.apply([], this.item);
18670             
18671             this.okBtn.show();
18672             this.cancelBtn.show();
18673             this.trigger.hide();
18674             
18675             if(this.editable){
18676                 this.tickableInputEl().focus();
18677             }
18678             
18679         }
18680         
18681         Roo.get(document).on('mousedown', this.collapseIf, this);
18682         Roo.get(document).on('mousewheel', this.collapseIf, this);
18683         if (!this.editable) {
18684             Roo.get(document).on('keydown', this.listKeyPress, this);
18685         }
18686         
18687         this.fireEvent('expand', this);
18688     },
18689
18690     // private
18691     // Implements the default empty TriggerField.onTriggerClick function
18692     onTriggerClick : function(e)
18693     {
18694         Roo.log('trigger click');
18695         
18696         if(this.disabled || !this.triggerList){
18697             return;
18698         }
18699         
18700         this.page = 0;
18701         this.loadNext = false;
18702         
18703         if(this.isExpanded()){
18704             this.collapse();
18705             if (!this.blockFocus) {
18706                 this.inputEl().focus();
18707             }
18708             
18709         }else {
18710             this.hasFocus = true;
18711             if(this.triggerAction == 'all') {
18712                 this.doQuery(this.allQuery, true);
18713             } else {
18714                 this.doQuery(this.getRawValue());
18715             }
18716             if (!this.blockFocus) {
18717                 this.inputEl().focus();
18718             }
18719         }
18720     },
18721     
18722     onTickableTriggerClick : function(e)
18723     {
18724         if(this.disabled){
18725             return;
18726         }
18727         
18728         this.page = 0;
18729         this.loadNext = false;
18730         this.hasFocus = true;
18731         
18732         if(this.triggerAction == 'all') {
18733             this.doQuery(this.allQuery, true);
18734         } else {
18735             this.doQuery(this.getRawValue());
18736         }
18737     },
18738     
18739     onSearchFieldClick : function(e)
18740     {
18741         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18742             this.onTickableFooterButtonClick(e, false, false);
18743             return;
18744         }
18745         
18746         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18747             return;
18748         }
18749         
18750         this.page = 0;
18751         this.loadNext = false;
18752         this.hasFocus = true;
18753         
18754         if(this.triggerAction == 'all') {
18755             this.doQuery(this.allQuery, true);
18756         } else {
18757             this.doQuery(this.getRawValue());
18758         }
18759     },
18760     
18761     listKeyPress : function(e)
18762     {
18763         //Roo.log('listkeypress');
18764         // scroll to first matching element based on key pres..
18765         if (e.isSpecialKey()) {
18766             return false;
18767         }
18768         var k = String.fromCharCode(e.getKey()).toUpperCase();
18769         //Roo.log(k);
18770         var match  = false;
18771         var csel = this.view.getSelectedNodes();
18772         var cselitem = false;
18773         if (csel.length) {
18774             var ix = this.view.indexOf(csel[0]);
18775             cselitem  = this.store.getAt(ix);
18776             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18777                 cselitem = false;
18778             }
18779             
18780         }
18781         
18782         this.store.each(function(v) { 
18783             if (cselitem) {
18784                 // start at existing selection.
18785                 if (cselitem.id == v.id) {
18786                     cselitem = false;
18787                 }
18788                 return true;
18789             }
18790                 
18791             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18792                 match = this.store.indexOf(v);
18793                 return false;
18794             }
18795             return true;
18796         }, this);
18797         
18798         if (match === false) {
18799             return true; // no more action?
18800         }
18801         // scroll to?
18802         this.view.select(match);
18803         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18804         sn.scrollIntoView(sn.dom.parentNode, false);
18805     },
18806     
18807     onViewScroll : function(e, t){
18808         
18809         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){
18810             return;
18811         }
18812         
18813         this.hasQuery = true;
18814         
18815         this.loading = this.list.select('.loading', true).first();
18816         
18817         if(this.loading === null){
18818             this.list.createChild({
18819                 tag: 'div',
18820                 cls: 'loading roo-select2-more-results roo-select2-active',
18821                 html: 'Loading more results...'
18822             });
18823             
18824             this.loading = this.list.select('.loading', true).first();
18825             
18826             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18827             
18828             this.loading.hide();
18829         }
18830         
18831         this.loading.show();
18832         
18833         var _combo = this;
18834         
18835         this.page++;
18836         this.loadNext = true;
18837         
18838         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18839         
18840         return;
18841     },
18842     
18843     addItem : function(o)
18844     {   
18845         var dv = ''; // display value
18846         
18847         if (this.displayField) {
18848             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18849         } else {
18850             // this is an error condition!!!
18851             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18852         }
18853         
18854         if(!dv.length){
18855             return;
18856         }
18857         
18858         var choice = this.choices.createChild({
18859             tag: 'li',
18860             cls: 'roo-select2-search-choice',
18861             cn: [
18862                 {
18863                     tag: 'div',
18864                     html: dv
18865                 },
18866                 {
18867                     tag: 'a',
18868                     href: '#',
18869                     cls: 'roo-select2-search-choice-close fa fa-times',
18870                     tabindex: '-1'
18871                 }
18872             ]
18873             
18874         }, this.searchField);
18875         
18876         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18877         
18878         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18879         
18880         this.item.push(o);
18881         
18882         this.lastData = o;
18883         
18884         this.syncValue();
18885         
18886         this.inputEl().dom.value = '';
18887         
18888         this.validate();
18889     },
18890     
18891     onRemoveItem : function(e, _self, o)
18892     {
18893         e.preventDefault();
18894         
18895         this.lastItem = Roo.apply([], this.item);
18896         
18897         var index = this.item.indexOf(o.data) * 1;
18898         
18899         if( index < 0){
18900             Roo.log('not this item?!');
18901             return;
18902         }
18903         
18904         this.item.splice(index, 1);
18905         o.item.remove();
18906         
18907         this.syncValue();
18908         
18909         this.fireEvent('remove', this, e);
18910         
18911         this.validate();
18912         
18913     },
18914     
18915     syncValue : function()
18916     {
18917         if(!this.item.length){
18918             this.clearValue();
18919             return;
18920         }
18921             
18922         var value = [];
18923         var _this = this;
18924         Roo.each(this.item, function(i){
18925             if(_this.valueField){
18926                 value.push(i[_this.valueField]);
18927                 return;
18928             }
18929
18930             value.push(i);
18931         });
18932
18933         this.value = value.join(',');
18934
18935         if(this.hiddenField){
18936             this.hiddenField.dom.value = this.value;
18937         }
18938         
18939         this.store.fireEvent("datachanged", this.store);
18940         
18941         this.validate();
18942     },
18943     
18944     clearItem : function()
18945     {
18946         if(!this.multiple){
18947             return;
18948         }
18949         
18950         this.item = [];
18951         
18952         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18953            c.remove();
18954         });
18955         
18956         this.syncValue();
18957         
18958         this.validate();
18959         
18960         if(this.tickable && !Roo.isTouch){
18961             this.view.refresh();
18962         }
18963     },
18964     
18965     inputEl: function ()
18966     {
18967         if(Roo.isIOS && this.useNativeIOS){
18968             return this.el.select('select.roo-ios-select', true).first();
18969         }
18970         
18971         if(Roo.isTouch && this.mobileTouchView){
18972             return this.el.select('input.form-control',true).first();
18973         }
18974         
18975         if(this.tickable){
18976             return this.searchField;
18977         }
18978         
18979         return this.el.select('input.form-control',true).first();
18980     },
18981     
18982     onTickableFooterButtonClick : function(e, btn, el)
18983     {
18984         e.preventDefault();
18985         
18986         this.lastItem = Roo.apply([], this.item);
18987         
18988         if(btn && btn.name == 'cancel'){
18989             this.tickItems = Roo.apply([], this.item);
18990             this.collapse();
18991             return;
18992         }
18993         
18994         this.clearItem();
18995         
18996         var _this = this;
18997         
18998         Roo.each(this.tickItems, function(o){
18999             _this.addItem(o);
19000         });
19001         
19002         this.collapse();
19003         
19004     },
19005     
19006     validate : function()
19007     {
19008         if(this.getVisibilityEl().hasClass('hidden')){
19009             return true;
19010         }
19011         
19012         var v = this.getRawValue();
19013         
19014         if(this.multiple){
19015             v = this.getValue();
19016         }
19017         
19018         if(this.disabled || this.allowBlank || v.length){
19019             this.markValid();
19020             return true;
19021         }
19022         
19023         this.markInvalid();
19024         return false;
19025     },
19026     
19027     tickableInputEl : function()
19028     {
19029         if(!this.tickable || !this.editable){
19030             return this.inputEl();
19031         }
19032         
19033         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19034     },
19035     
19036     
19037     getAutoCreateTouchView : function()
19038     {
19039         var id = Roo.id();
19040         
19041         var cfg = {
19042             cls: 'form-group' //input-group
19043         };
19044         
19045         var input =  {
19046             tag: 'input',
19047             id : id,
19048             type : this.inputType,
19049             cls : 'form-control x-combo-noedit',
19050             autocomplete: 'new-password',
19051             placeholder : this.placeholder || '',
19052             readonly : true
19053         };
19054         
19055         if (this.name) {
19056             input.name = this.name;
19057         }
19058         
19059         if (this.size) {
19060             input.cls += ' input-' + this.size;
19061         }
19062         
19063         if (this.disabled) {
19064             input.disabled = true;
19065         }
19066         
19067         var inputblock = {
19068             cls : 'roo-combobox-wrap',
19069             cn : [
19070                 input
19071             ]
19072         };
19073         
19074         if(this.before){
19075             inputblock.cls += ' input-group';
19076             
19077             inputblock.cn.unshift({
19078                 tag :'span',
19079                 cls : 'input-group-addon input-group-prepend input-group-text',
19080                 html : this.before
19081             });
19082         }
19083         
19084         if(this.removable && !this.multiple){
19085             inputblock.cls += ' roo-removable';
19086             
19087             inputblock.cn.push({
19088                 tag: 'button',
19089                 html : 'x',
19090                 cls : 'roo-combo-removable-btn close'
19091             });
19092         }
19093
19094         if(this.hasFeedback && !this.allowBlank){
19095             
19096             inputblock.cls += ' has-feedback';
19097             
19098             inputblock.cn.push({
19099                 tag: 'span',
19100                 cls: 'glyphicon form-control-feedback'
19101             });
19102             
19103         }
19104         
19105         if (this.after) {
19106             
19107             inputblock.cls += (this.before) ? '' : ' input-group';
19108             
19109             inputblock.cn.push({
19110                 tag :'span',
19111                 cls : 'input-group-addon input-group-append input-group-text',
19112                 html : this.after
19113             });
19114         }
19115
19116         
19117         var ibwrap = inputblock;
19118         
19119         if(this.multiple){
19120             ibwrap = {
19121                 tag: 'ul',
19122                 cls: 'roo-select2-choices',
19123                 cn:[
19124                     {
19125                         tag: 'li',
19126                         cls: 'roo-select2-search-field',
19127                         cn: [
19128
19129                             inputblock
19130                         ]
19131                     }
19132                 ]
19133             };
19134         
19135             
19136         }
19137         
19138         var combobox = {
19139             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19140             cn: [
19141                 {
19142                     tag: 'input',
19143                     type : 'hidden',
19144                     cls: 'form-hidden-field'
19145                 },
19146                 ibwrap
19147             ]
19148         };
19149         
19150         if(!this.multiple && this.showToggleBtn){
19151             
19152             var caret = {
19153                 cls: 'caret'
19154             };
19155             
19156             if (this.caret != false) {
19157                 caret = {
19158                      tag: 'i',
19159                      cls: 'fa fa-' + this.caret
19160                 };
19161                 
19162             }
19163             
19164             combobox.cn.push({
19165                 tag :'span',
19166                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19167                 cn : [
19168                     Roo.bootstrap.version == 3 ? caret : '',
19169                     {
19170                         tag: 'span',
19171                         cls: 'combobox-clear',
19172                         cn  : [
19173                             {
19174                                 tag : 'i',
19175                                 cls: 'icon-remove'
19176                             }
19177                         ]
19178                     }
19179                 ]
19180
19181             })
19182         }
19183         
19184         if(this.multiple){
19185             combobox.cls += ' roo-select2-container-multi';
19186         }
19187         
19188         var required =  this.allowBlank ?  {
19189                     tag : 'i',
19190                     style: 'display: none'
19191                 } : {
19192                    tag : 'i',
19193                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19194                    tooltip : 'This field is required'
19195                 };
19196         
19197         var align = this.labelAlign || this.parentLabelAlign();
19198         
19199         if (align ==='left' && this.fieldLabel.length) {
19200
19201             cfg.cn = [
19202                 required,
19203                 {
19204                     tag: 'label',
19205                     cls : 'control-label col-form-label',
19206                     html : this.fieldLabel
19207
19208                 },
19209                 {
19210                     cls : 'roo-combobox-wrap ', 
19211                     cn: [
19212                         combobox
19213                     ]
19214                 }
19215             ];
19216             
19217             var labelCfg = cfg.cn[1];
19218             var contentCfg = cfg.cn[2];
19219             
19220
19221             if(this.indicatorpos == 'right'){
19222                 cfg.cn = [
19223                     {
19224                         tag: 'label',
19225                         'for' :  id,
19226                         cls : 'control-label col-form-label',
19227                         cn : [
19228                             {
19229                                 tag : 'span',
19230                                 html : this.fieldLabel
19231                             },
19232                             required
19233                         ]
19234                     },
19235                     {
19236                         cls : "roo-combobox-wrap ",
19237                         cn: [
19238                             combobox
19239                         ]
19240                     }
19241
19242                 ];
19243                 
19244                 labelCfg = cfg.cn[0];
19245                 contentCfg = cfg.cn[1];
19246             }
19247             
19248            
19249             
19250             if(this.labelWidth > 12){
19251                 labelCfg.style = "width: " + this.labelWidth + 'px';
19252             }
19253            
19254             if(this.labelWidth < 13 && this.labelmd == 0){
19255                 this.labelmd = this.labelWidth;
19256             }
19257             
19258             if(this.labellg > 0){
19259                 labelCfg.cls += ' col-lg-' + this.labellg;
19260                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19261             }
19262             
19263             if(this.labelmd > 0){
19264                 labelCfg.cls += ' col-md-' + this.labelmd;
19265                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19266             }
19267             
19268             if(this.labelsm > 0){
19269                 labelCfg.cls += ' col-sm-' + this.labelsm;
19270                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19271             }
19272             
19273             if(this.labelxs > 0){
19274                 labelCfg.cls += ' col-xs-' + this.labelxs;
19275                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19276             }
19277                 
19278                 
19279         } else if ( this.fieldLabel.length) {
19280             cfg.cn = [
19281                required,
19282                 {
19283                     tag: 'label',
19284                     cls : 'control-label',
19285                     html : this.fieldLabel
19286
19287                 },
19288                 {
19289                     cls : '', 
19290                     cn: [
19291                         combobox
19292                     ]
19293                 }
19294             ];
19295             
19296             if(this.indicatorpos == 'right'){
19297                 cfg.cn = [
19298                     {
19299                         tag: 'label',
19300                         cls : 'control-label',
19301                         html : this.fieldLabel,
19302                         cn : [
19303                             required
19304                         ]
19305                     },
19306                     {
19307                         cls : '', 
19308                         cn: [
19309                             combobox
19310                         ]
19311                     }
19312                 ];
19313             }
19314         } else {
19315             cfg.cn = combobox;    
19316         }
19317         
19318         
19319         var settings = this;
19320         
19321         ['xs','sm','md','lg'].map(function(size){
19322             if (settings[size]) {
19323                 cfg.cls += ' col-' + size + '-' + settings[size];
19324             }
19325         });
19326         
19327         return cfg;
19328     },
19329     
19330     initTouchView : function()
19331     {
19332         this.renderTouchView();
19333         
19334         this.touchViewEl.on('scroll', function(){
19335             this.el.dom.scrollTop = 0;
19336         }, this);
19337         
19338         this.originalValue = this.getValue();
19339         
19340         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19341         
19342         this.inputEl().on("click", this.showTouchView, this);
19343         if (this.triggerEl) {
19344             this.triggerEl.on("click", this.showTouchView, this);
19345         }
19346         
19347         
19348         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19349         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19350         
19351         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19352         
19353         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19354         this.store.on('load', this.onTouchViewLoad, this);
19355         this.store.on('loadexception', this.onTouchViewLoadException, this);
19356         
19357         if(this.hiddenName){
19358             
19359             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19360             
19361             this.hiddenField.dom.value =
19362                 this.hiddenValue !== undefined ? this.hiddenValue :
19363                 this.value !== undefined ? this.value : '';
19364         
19365             this.el.dom.removeAttribute('name');
19366             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19367         }
19368         
19369         if(this.multiple){
19370             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19371             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19372         }
19373         
19374         if(this.removable && !this.multiple){
19375             var close = this.closeTriggerEl();
19376             if(close){
19377                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19378                 close.on('click', this.removeBtnClick, this, close);
19379             }
19380         }
19381         /*
19382          * fix the bug in Safari iOS8
19383          */
19384         this.inputEl().on("focus", function(e){
19385             document.activeElement.blur();
19386         }, this);
19387         
19388         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19389         
19390         return;
19391         
19392         
19393     },
19394     
19395     renderTouchView : function()
19396     {
19397         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19398         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19399         
19400         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19401         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19402         
19403         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19404         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19405         this.touchViewBodyEl.setStyle('overflow', 'auto');
19406         
19407         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19408         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19409         
19410         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19411         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19412         
19413     },
19414     
19415     showTouchView : function()
19416     {
19417         if(this.disabled){
19418             return;
19419         }
19420         
19421         this.touchViewHeaderEl.hide();
19422
19423         if(this.modalTitle.length){
19424             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19425             this.touchViewHeaderEl.show();
19426         }
19427
19428         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19429         this.touchViewEl.show();
19430
19431         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19432         
19433         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19434         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19435
19436         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19437
19438         if(this.modalTitle.length){
19439             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19440         }
19441         
19442         this.touchViewBodyEl.setHeight(bodyHeight);
19443
19444         if(this.animate){
19445             var _this = this;
19446             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19447         }else{
19448             this.touchViewEl.addClass(['in','show']);
19449         }
19450         
19451         if(this._touchViewMask){
19452             Roo.get(document.body).addClass("x-body-masked");
19453             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19454             this._touchViewMask.setStyle('z-index', 10000);
19455             this._touchViewMask.addClass('show');
19456         }
19457         
19458         this.doTouchViewQuery();
19459         
19460     },
19461     
19462     hideTouchView : function()
19463     {
19464         this.touchViewEl.removeClass(['in','show']);
19465
19466         if(this.animate){
19467             var _this = this;
19468             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19469         }else{
19470             this.touchViewEl.setStyle('display', 'none');
19471         }
19472         
19473         if(this._touchViewMask){
19474             this._touchViewMask.removeClass('show');
19475             Roo.get(document.body).removeClass("x-body-masked");
19476         }
19477     },
19478     
19479     setTouchViewValue : function()
19480     {
19481         if(this.multiple){
19482             this.clearItem();
19483         
19484             var _this = this;
19485
19486             Roo.each(this.tickItems, function(o){
19487                 this.addItem(o);
19488             }, this);
19489         }
19490         
19491         this.hideTouchView();
19492     },
19493     
19494     doTouchViewQuery : function()
19495     {
19496         var qe = {
19497             query: '',
19498             forceAll: true,
19499             combo: this,
19500             cancel:false
19501         };
19502         
19503         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19504             return false;
19505         }
19506         
19507         if(!this.alwaysQuery || this.mode == 'local'){
19508             this.onTouchViewLoad();
19509             return;
19510         }
19511         
19512         this.store.load();
19513     },
19514     
19515     onTouchViewBeforeLoad : function(combo,opts)
19516     {
19517         return;
19518     },
19519
19520     // private
19521     onTouchViewLoad : function()
19522     {
19523         if(this.store.getCount() < 1){
19524             this.onTouchViewEmptyResults();
19525             return;
19526         }
19527         
19528         this.clearTouchView();
19529         
19530         var rawValue = this.getRawValue();
19531         
19532         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19533         
19534         this.tickItems = [];
19535         
19536         this.store.data.each(function(d, rowIndex){
19537             var row = this.touchViewListGroup.createChild(template);
19538             
19539             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19540                 row.addClass(d.data.cls);
19541             }
19542             
19543             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19544                 var cfg = {
19545                     data : d.data,
19546                     html : d.data[this.displayField]
19547                 };
19548                 
19549                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19550                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19551                 }
19552             }
19553             row.removeClass('selected');
19554             if(!this.multiple && this.valueField &&
19555                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19556             {
19557                 // radio buttons..
19558                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19559                 row.addClass('selected');
19560             }
19561             
19562             if(this.multiple && this.valueField &&
19563                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19564             {
19565                 
19566                 // checkboxes...
19567                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19568                 this.tickItems.push(d.data);
19569             }
19570             
19571             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19572             
19573         }, this);
19574         
19575         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19576         
19577         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19578
19579         if(this.modalTitle.length){
19580             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19581         }
19582
19583         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19584         
19585         if(this.mobile_restrict_height && listHeight < bodyHeight){
19586             this.touchViewBodyEl.setHeight(listHeight);
19587         }
19588         
19589         var _this = this;
19590         
19591         if(firstChecked && listHeight > bodyHeight){
19592             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19593         }
19594         
19595     },
19596     
19597     onTouchViewLoadException : function()
19598     {
19599         this.hideTouchView();
19600     },
19601     
19602     onTouchViewEmptyResults : function()
19603     {
19604         this.clearTouchView();
19605         
19606         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19607         
19608         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19609         
19610     },
19611     
19612     clearTouchView : function()
19613     {
19614         this.touchViewListGroup.dom.innerHTML = '';
19615     },
19616     
19617     onTouchViewClick : function(e, el, o)
19618     {
19619         e.preventDefault();
19620         
19621         var row = o.row;
19622         var rowIndex = o.rowIndex;
19623         
19624         var r = this.store.getAt(rowIndex);
19625         
19626         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19627             
19628             if(!this.multiple){
19629                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19630                     c.dom.removeAttribute('checked');
19631                 }, this);
19632
19633                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19634
19635                 this.setFromData(r.data);
19636
19637                 var close = this.closeTriggerEl();
19638
19639                 if(close){
19640                     close.show();
19641                 }
19642
19643                 this.hideTouchView();
19644
19645                 this.fireEvent('select', this, r, rowIndex);
19646
19647                 return;
19648             }
19649
19650             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19651                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19652                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19653                 return;
19654             }
19655
19656             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19657             this.addItem(r.data);
19658             this.tickItems.push(r.data);
19659         }
19660     },
19661     
19662     getAutoCreateNativeIOS : function()
19663     {
19664         var cfg = {
19665             cls: 'form-group' //input-group,
19666         };
19667         
19668         var combobox =  {
19669             tag: 'select',
19670             cls : 'roo-ios-select'
19671         };
19672         
19673         if (this.name) {
19674             combobox.name = this.name;
19675         }
19676         
19677         if (this.disabled) {
19678             combobox.disabled = true;
19679         }
19680         
19681         var settings = this;
19682         
19683         ['xs','sm','md','lg'].map(function(size){
19684             if (settings[size]) {
19685                 cfg.cls += ' col-' + size + '-' + settings[size];
19686             }
19687         });
19688         
19689         cfg.cn = combobox;
19690         
19691         return cfg;
19692         
19693     },
19694     
19695     initIOSView : function()
19696     {
19697         this.store.on('load', this.onIOSViewLoad, this);
19698         
19699         return;
19700     },
19701     
19702     onIOSViewLoad : function()
19703     {
19704         if(this.store.getCount() < 1){
19705             return;
19706         }
19707         
19708         this.clearIOSView();
19709         
19710         if(this.allowBlank) {
19711             
19712             var default_text = '-- SELECT --';
19713             
19714             if(this.placeholder.length){
19715                 default_text = this.placeholder;
19716             }
19717             
19718             if(this.emptyTitle.length){
19719                 default_text += ' - ' + this.emptyTitle + ' -';
19720             }
19721             
19722             var opt = this.inputEl().createChild({
19723                 tag: 'option',
19724                 value : 0,
19725                 html : default_text
19726             });
19727             
19728             var o = {};
19729             o[this.valueField] = 0;
19730             o[this.displayField] = default_text;
19731             
19732             this.ios_options.push({
19733                 data : o,
19734                 el : opt
19735             });
19736             
19737         }
19738         
19739         this.store.data.each(function(d, rowIndex){
19740             
19741             var html = '';
19742             
19743             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19744                 html = d.data[this.displayField];
19745             }
19746             
19747             var value = '';
19748             
19749             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19750                 value = d.data[this.valueField];
19751             }
19752             
19753             var option = {
19754                 tag: 'option',
19755                 value : value,
19756                 html : html
19757             };
19758             
19759             if(this.value == d.data[this.valueField]){
19760                 option['selected'] = true;
19761             }
19762             
19763             var opt = this.inputEl().createChild(option);
19764             
19765             this.ios_options.push({
19766                 data : d.data,
19767                 el : opt
19768             });
19769             
19770         }, this);
19771         
19772         this.inputEl().on('change', function(){
19773            this.fireEvent('select', this);
19774         }, this);
19775         
19776     },
19777     
19778     clearIOSView: function()
19779     {
19780         this.inputEl().dom.innerHTML = '';
19781         
19782         this.ios_options = [];
19783     },
19784     
19785     setIOSValue: function(v)
19786     {
19787         this.value = v;
19788         
19789         if(!this.ios_options){
19790             return;
19791         }
19792         
19793         Roo.each(this.ios_options, function(opts){
19794            
19795            opts.el.dom.removeAttribute('selected');
19796            
19797            if(opts.data[this.valueField] != v){
19798                return;
19799            }
19800            
19801            opts.el.dom.setAttribute('selected', true);
19802            
19803         }, this);
19804     }
19805
19806     /** 
19807     * @cfg {Boolean} grow 
19808     * @hide 
19809     */
19810     /** 
19811     * @cfg {Number} growMin 
19812     * @hide 
19813     */
19814     /** 
19815     * @cfg {Number} growMax 
19816     * @hide 
19817     */
19818     /**
19819      * @hide
19820      * @method autoSize
19821      */
19822 });
19823
19824 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19825     
19826     header : {
19827         tag: 'div',
19828         cls: 'modal-header',
19829         cn: [
19830             {
19831                 tag: 'h4',
19832                 cls: 'modal-title'
19833             }
19834         ]
19835     },
19836     
19837     body : {
19838         tag: 'div',
19839         cls: 'modal-body',
19840         cn: [
19841             {
19842                 tag: 'ul',
19843                 cls: 'list-group'
19844             }
19845         ]
19846     },
19847     
19848     listItemRadio : {
19849         tag: 'li',
19850         cls: 'list-group-item',
19851         cn: [
19852             {
19853                 tag: 'span',
19854                 cls: 'roo-combobox-list-group-item-value'
19855             },
19856             {
19857                 tag: 'div',
19858                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19859                 cn: [
19860                     {
19861                         tag: 'input',
19862                         type: 'radio'
19863                     },
19864                     {
19865                         tag: 'label'
19866                     }
19867                 ]
19868             }
19869         ]
19870     },
19871     
19872     listItemCheckbox : {
19873         tag: 'li',
19874         cls: 'list-group-item',
19875         cn: [
19876             {
19877                 tag: 'span',
19878                 cls: 'roo-combobox-list-group-item-value'
19879             },
19880             {
19881                 tag: 'div',
19882                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19883                 cn: [
19884                     {
19885                         tag: 'input',
19886                         type: 'checkbox'
19887                     },
19888                     {
19889                         tag: 'label'
19890                     }
19891                 ]
19892             }
19893         ]
19894     },
19895     
19896     emptyResult : {
19897         tag: 'div',
19898         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19899     },
19900     
19901     footer : {
19902         tag: 'div',
19903         cls: 'modal-footer',
19904         cn: [
19905             {
19906                 tag: 'div',
19907                 cls: 'row',
19908                 cn: [
19909                     {
19910                         tag: 'div',
19911                         cls: 'col-xs-6 text-left',
19912                         cn: {
19913                             tag: 'button',
19914                             cls: 'btn btn-danger roo-touch-view-cancel',
19915                             html: 'Cancel'
19916                         }
19917                     },
19918                     {
19919                         tag: 'div',
19920                         cls: 'col-xs-6 text-right',
19921                         cn: {
19922                             tag: 'button',
19923                             cls: 'btn btn-success roo-touch-view-ok',
19924                             html: 'OK'
19925                         }
19926                     }
19927                 ]
19928             }
19929         ]
19930         
19931     }
19932 });
19933
19934 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19935     
19936     touchViewTemplate : {
19937         tag: 'div',
19938         cls: 'modal fade roo-combobox-touch-view',
19939         cn: [
19940             {
19941                 tag: 'div',
19942                 cls: 'modal-dialog',
19943                 style : 'position:fixed', // we have to fix position....
19944                 cn: [
19945                     {
19946                         tag: 'div',
19947                         cls: 'modal-content',
19948                         cn: [
19949                             Roo.bootstrap.form.ComboBox.header,
19950                             Roo.bootstrap.form.ComboBox.body,
19951                             Roo.bootstrap.form.ComboBox.footer
19952                         ]
19953                     }
19954                 ]
19955             }
19956         ]
19957     }
19958 });/*
19959  * Based on:
19960  * Ext JS Library 1.1.1
19961  * Copyright(c) 2006-2007, Ext JS, LLC.
19962  *
19963  * Originally Released Under LGPL - original licence link has changed is not relivant.
19964  *
19965  * Fork - LGPL
19966  * <script type="text/javascript">
19967  */
19968
19969 /**
19970  * @class Roo.View
19971  * @extends Roo.util.Observable
19972  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19973  * This class also supports single and multi selection modes. <br>
19974  * Create a data model bound view:
19975  <pre><code>
19976  var store = new Roo.data.Store(...);
19977
19978  var view = new Roo.View({
19979     el : "my-element",
19980     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19981  
19982     singleSelect: true,
19983     selectedClass: "ydataview-selected",
19984     store: store
19985  });
19986
19987  // listen for node click?
19988  view.on("click", function(vw, index, node, e){
19989  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19990  });
19991
19992  // load XML data
19993  dataModel.load("foobar.xml");
19994  </code></pre>
19995  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19996  * <br><br>
19997  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19998  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19999  * 
20000  * Note: old style constructor is still suported (container, template, config)
20001  * 
20002  * @constructor
20003  * Create a new View
20004  * @param {Object} config The config object
20005  * 
20006  */
20007 Roo.View = function(config, depreciated_tpl, depreciated_config){
20008     
20009     this.parent = false;
20010     
20011     if (typeof(depreciated_tpl) == 'undefined') {
20012         // new way.. - universal constructor.
20013         Roo.apply(this, config);
20014         this.el  = Roo.get(this.el);
20015     } else {
20016         // old format..
20017         this.el  = Roo.get(config);
20018         this.tpl = depreciated_tpl;
20019         Roo.apply(this, depreciated_config);
20020     }
20021     this.wrapEl  = this.el.wrap().wrap();
20022     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20023     
20024     
20025     if(typeof(this.tpl) == "string"){
20026         this.tpl = new Roo.Template(this.tpl);
20027     } else {
20028         // support xtype ctors..
20029         this.tpl = new Roo.factory(this.tpl, Roo);
20030     }
20031     
20032     
20033     this.tpl.compile();
20034     
20035     /** @private */
20036     this.addEvents({
20037         /**
20038          * @event beforeclick
20039          * Fires before a click is processed. Returns false to cancel the default action.
20040          * @param {Roo.View} this
20041          * @param {Number} index The index of the target node
20042          * @param {HTMLElement} node The target node
20043          * @param {Roo.EventObject} e The raw event object
20044          */
20045             "beforeclick" : true,
20046         /**
20047          * @event click
20048          * Fires when a template node is clicked.
20049          * @param {Roo.View} this
20050          * @param {Number} index The index of the target node
20051          * @param {HTMLElement} node The target node
20052          * @param {Roo.EventObject} e The raw event object
20053          */
20054             "click" : true,
20055         /**
20056          * @event dblclick
20057          * Fires when a template node is double clicked.
20058          * @param {Roo.View} this
20059          * @param {Number} index The index of the target node
20060          * @param {HTMLElement} node The target node
20061          * @param {Roo.EventObject} e The raw event object
20062          */
20063             "dblclick" : true,
20064         /**
20065          * @event contextmenu
20066          * Fires when a template node is right clicked.
20067          * @param {Roo.View} this
20068          * @param {Number} index The index of the target node
20069          * @param {HTMLElement} node The target node
20070          * @param {Roo.EventObject} e The raw event object
20071          */
20072             "contextmenu" : true,
20073         /**
20074          * @event selectionchange
20075          * Fires when the selected nodes change.
20076          * @param {Roo.View} this
20077          * @param {Array} selections Array of the selected nodes
20078          */
20079             "selectionchange" : true,
20080     
20081         /**
20082          * @event beforeselect
20083          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20084          * @param {Roo.View} this
20085          * @param {HTMLElement} node The node to be selected
20086          * @param {Array} selections Array of currently selected nodes
20087          */
20088             "beforeselect" : true,
20089         /**
20090          * @event preparedata
20091          * Fires on every row to render, to allow you to change the data.
20092          * @param {Roo.View} this
20093          * @param {Object} data to be rendered (change this)
20094          */
20095           "preparedata" : true
20096           
20097           
20098         });
20099
20100
20101
20102     this.el.on({
20103         "click": this.onClick,
20104         "dblclick": this.onDblClick,
20105         "contextmenu": this.onContextMenu,
20106         scope:this
20107     });
20108
20109     this.selections = [];
20110     this.nodes = [];
20111     this.cmp = new Roo.CompositeElementLite([]);
20112     if(this.store){
20113         this.store = Roo.factory(this.store, Roo.data);
20114         this.setStore(this.store, true);
20115     }
20116     
20117     if ( this.footer && this.footer.xtype) {
20118            
20119          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20120         
20121         this.footer.dataSource = this.store;
20122         this.footer.container = fctr;
20123         this.footer = Roo.factory(this.footer, Roo);
20124         fctr.insertFirst(this.el);
20125         
20126         // this is a bit insane - as the paging toolbar seems to detach the el..
20127 //        dom.parentNode.parentNode.parentNode
20128          // they get detached?
20129     }
20130     
20131     
20132     Roo.View.superclass.constructor.call(this);
20133     
20134     
20135 };
20136
20137 Roo.extend(Roo.View, Roo.util.Observable, {
20138     
20139      /**
20140      * @cfg {Roo.data.Store} store Data store to load data from.
20141      */
20142     store : false,
20143     
20144     /**
20145      * @cfg {String|Roo.Element} el The container element.
20146      */
20147     el : '',
20148     
20149     /**
20150      * @cfg {String|Roo.Template} tpl The template used by this View 
20151      */
20152     tpl : false,
20153     /**
20154      * @cfg {String} dataName the named area of the template to use as the data area
20155      *                          Works with domtemplates roo-name="name"
20156      */
20157     dataName: false,
20158     /**
20159      * @cfg {String} selectedClass The css class to add to selected nodes
20160      */
20161     selectedClass : "x-view-selected",
20162      /**
20163      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20164      */
20165     emptyText : "",
20166     
20167     /**
20168      * @cfg {String} text to display on mask (default Loading)
20169      */
20170     mask : false,
20171     /**
20172      * @cfg {Boolean} multiSelect Allow multiple selection
20173      */
20174     multiSelect : false,
20175     /**
20176      * @cfg {Boolean} singleSelect Allow single selection
20177      */
20178     singleSelect:  false,
20179     
20180     /**
20181      * @cfg {Boolean} toggleSelect - selecting 
20182      */
20183     toggleSelect : false,
20184     
20185     /**
20186      * @cfg {Boolean} tickable - selecting 
20187      */
20188     tickable : false,
20189     
20190     /**
20191      * Returns the element this view is bound to.
20192      * @return {Roo.Element}
20193      */
20194     getEl : function(){
20195         return this.wrapEl;
20196     },
20197     
20198     
20199
20200     /**
20201      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20202      */
20203     refresh : function(){
20204         //Roo.log('refresh');
20205         var t = this.tpl;
20206         
20207         // if we are using something like 'domtemplate', then
20208         // the what gets used is:
20209         // t.applySubtemplate(NAME, data, wrapping data..)
20210         // the outer template then get' applied with
20211         //     the store 'extra data'
20212         // and the body get's added to the
20213         //      roo-name="data" node?
20214         //      <span class='roo-tpl-{name}'></span> ?????
20215         
20216         
20217         
20218         this.clearSelections();
20219         this.el.update("");
20220         var html = [];
20221         var records = this.store.getRange();
20222         if(records.length < 1) {
20223             
20224             // is this valid??  = should it render a template??
20225             
20226             this.el.update(this.emptyText);
20227             return;
20228         }
20229         var el = this.el;
20230         if (this.dataName) {
20231             this.el.update(t.apply(this.store.meta)); //????
20232             el = this.el.child('.roo-tpl-' + this.dataName);
20233         }
20234         
20235         for(var i = 0, len = records.length; i < len; i++){
20236             var data = this.prepareData(records[i].data, i, records[i]);
20237             this.fireEvent("preparedata", this, data, i, records[i]);
20238             
20239             var d = Roo.apply({}, data);
20240             
20241             if(this.tickable){
20242                 Roo.apply(d, {'roo-id' : Roo.id()});
20243                 
20244                 var _this = this;
20245             
20246                 Roo.each(this.parent.item, function(item){
20247                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20248                         return;
20249                     }
20250                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20251                 });
20252             }
20253             
20254             html[html.length] = Roo.util.Format.trim(
20255                 this.dataName ?
20256                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20257                     t.apply(d)
20258             );
20259         }
20260         
20261         
20262         
20263         el.update(html.join(""));
20264         this.nodes = el.dom.childNodes;
20265         this.updateIndexes(0);
20266     },
20267     
20268
20269     /**
20270      * Function to override to reformat the data that is sent to
20271      * the template for each node.
20272      * DEPRICATED - use the preparedata event handler.
20273      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20274      * a JSON object for an UpdateManager bound view).
20275      */
20276     prepareData : function(data, index, record)
20277     {
20278         this.fireEvent("preparedata", this, data, index, record);
20279         return data;
20280     },
20281
20282     onUpdate : function(ds, record){
20283         // Roo.log('on update');   
20284         this.clearSelections();
20285         var index = this.store.indexOf(record);
20286         var n = this.nodes[index];
20287         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20288         n.parentNode.removeChild(n);
20289         this.updateIndexes(index, index);
20290     },
20291
20292     
20293     
20294 // --------- FIXME     
20295     onAdd : function(ds, records, index)
20296     {
20297         //Roo.log(['on Add', ds, records, index] );        
20298         this.clearSelections();
20299         if(this.nodes.length == 0){
20300             this.refresh();
20301             return;
20302         }
20303         var n = this.nodes[index];
20304         for(var i = 0, len = records.length; i < len; i++){
20305             var d = this.prepareData(records[i].data, i, records[i]);
20306             if(n){
20307                 this.tpl.insertBefore(n, d);
20308             }else{
20309                 
20310                 this.tpl.append(this.el, d);
20311             }
20312         }
20313         this.updateIndexes(index);
20314     },
20315
20316     onRemove : function(ds, record, index){
20317        // Roo.log('onRemove');
20318         this.clearSelections();
20319         var el = this.dataName  ?
20320             this.el.child('.roo-tpl-' + this.dataName) :
20321             this.el; 
20322         
20323         el.dom.removeChild(this.nodes[index]);
20324         this.updateIndexes(index);
20325     },
20326
20327     /**
20328      * Refresh an individual node.
20329      * @param {Number} index
20330      */
20331     refreshNode : function(index){
20332         this.onUpdate(this.store, this.store.getAt(index));
20333     },
20334
20335     updateIndexes : function(startIndex, endIndex){
20336         var ns = this.nodes;
20337         startIndex = startIndex || 0;
20338         endIndex = endIndex || ns.length - 1;
20339         for(var i = startIndex; i <= endIndex; i++){
20340             ns[i].nodeIndex = i;
20341         }
20342     },
20343
20344     /**
20345      * Changes the data store this view uses and refresh the view.
20346      * @param {Store} store
20347      */
20348     setStore : function(store, initial){
20349         if(!initial && this.store){
20350             this.store.un("datachanged", this.refresh);
20351             this.store.un("add", this.onAdd);
20352             this.store.un("remove", this.onRemove);
20353             this.store.un("update", this.onUpdate);
20354             this.store.un("clear", this.refresh);
20355             this.store.un("beforeload", this.onBeforeLoad);
20356             this.store.un("load", this.onLoad);
20357             this.store.un("loadexception", this.onLoad);
20358         }
20359         if(store){
20360           
20361             store.on("datachanged", this.refresh, this);
20362             store.on("add", this.onAdd, this);
20363             store.on("remove", this.onRemove, this);
20364             store.on("update", this.onUpdate, this);
20365             store.on("clear", this.refresh, this);
20366             store.on("beforeload", this.onBeforeLoad, this);
20367             store.on("load", this.onLoad, this);
20368             store.on("loadexception", this.onLoad, this);
20369         }
20370         
20371         if(store){
20372             this.refresh();
20373         }
20374     },
20375     /**
20376      * onbeforeLoad - masks the loading area.
20377      *
20378      */
20379     onBeforeLoad : function(store,opts)
20380     {
20381          //Roo.log('onBeforeLoad');   
20382         if (!opts.add) {
20383             this.el.update("");
20384         }
20385         this.el.mask(this.mask ? this.mask : "Loading" ); 
20386     },
20387     onLoad : function ()
20388     {
20389         this.el.unmask();
20390     },
20391     
20392
20393     /**
20394      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20395      * @param {HTMLElement} node
20396      * @return {HTMLElement} The template node
20397      */
20398     findItemFromChild : function(node){
20399         var el = this.dataName  ?
20400             this.el.child('.roo-tpl-' + this.dataName,true) :
20401             this.el.dom; 
20402         
20403         if(!node || node.parentNode == el){
20404                     return node;
20405             }
20406             var p = node.parentNode;
20407             while(p && p != el){
20408             if(p.parentNode == el){
20409                 return p;
20410             }
20411             p = p.parentNode;
20412         }
20413             return null;
20414     },
20415
20416     /** @ignore */
20417     onClick : function(e){
20418         var item = this.findItemFromChild(e.getTarget());
20419         if(item){
20420             var index = this.indexOf(item);
20421             if(this.onItemClick(item, index, e) !== false){
20422                 this.fireEvent("click", this, index, item, e);
20423             }
20424         }else{
20425             this.clearSelections();
20426         }
20427     },
20428
20429     /** @ignore */
20430     onContextMenu : function(e){
20431         var item = this.findItemFromChild(e.getTarget());
20432         if(item){
20433             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20434         }
20435     },
20436
20437     /** @ignore */
20438     onDblClick : function(e){
20439         var item = this.findItemFromChild(e.getTarget());
20440         if(item){
20441             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20442         }
20443     },
20444
20445     onItemClick : function(item, index, e)
20446     {
20447         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20448             return false;
20449         }
20450         if (this.toggleSelect) {
20451             var m = this.isSelected(item) ? 'unselect' : 'select';
20452             //Roo.log(m);
20453             var _t = this;
20454             _t[m](item, true, false);
20455             return true;
20456         }
20457         if(this.multiSelect || this.singleSelect){
20458             if(this.multiSelect && e.shiftKey && this.lastSelection){
20459                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20460             }else{
20461                 this.select(item, this.multiSelect && e.ctrlKey);
20462                 this.lastSelection = item;
20463             }
20464             
20465             if(!this.tickable){
20466                 e.preventDefault();
20467             }
20468             
20469         }
20470         return true;
20471     },
20472
20473     /**
20474      * Get the number of selected nodes.
20475      * @return {Number}
20476      */
20477     getSelectionCount : function(){
20478         return this.selections.length;
20479     },
20480
20481     /**
20482      * Get the currently selected nodes.
20483      * @return {Array} An array of HTMLElements
20484      */
20485     getSelectedNodes : function(){
20486         return this.selections;
20487     },
20488
20489     /**
20490      * Get the indexes of the selected nodes.
20491      * @return {Array}
20492      */
20493     getSelectedIndexes : function(){
20494         var indexes = [], s = this.selections;
20495         for(var i = 0, len = s.length; i < len; i++){
20496             indexes.push(s[i].nodeIndex);
20497         }
20498         return indexes;
20499     },
20500
20501     /**
20502      * Clear all selections
20503      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20504      */
20505     clearSelections : function(suppressEvent){
20506         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20507             this.cmp.elements = this.selections;
20508             this.cmp.removeClass(this.selectedClass);
20509             this.selections = [];
20510             if(!suppressEvent){
20511                 this.fireEvent("selectionchange", this, this.selections);
20512             }
20513         }
20514     },
20515
20516     /**
20517      * Returns true if the passed node is selected
20518      * @param {HTMLElement/Number} node The node or node index
20519      * @return {Boolean}
20520      */
20521     isSelected : function(node){
20522         var s = this.selections;
20523         if(s.length < 1){
20524             return false;
20525         }
20526         node = this.getNode(node);
20527         return s.indexOf(node) !== -1;
20528     },
20529
20530     /**
20531      * Selects nodes.
20532      * @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
20533      * @param {Boolean} keepExisting (optional) true to keep existing selections
20534      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20535      */
20536     select : function(nodeInfo, keepExisting, suppressEvent){
20537         if(nodeInfo instanceof Array){
20538             if(!keepExisting){
20539                 this.clearSelections(true);
20540             }
20541             for(var i = 0, len = nodeInfo.length; i < len; i++){
20542                 this.select(nodeInfo[i], true, true);
20543             }
20544             return;
20545         } 
20546         var node = this.getNode(nodeInfo);
20547         if(!node || this.isSelected(node)){
20548             return; // already selected.
20549         }
20550         if(!keepExisting){
20551             this.clearSelections(true);
20552         }
20553         
20554         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20555             Roo.fly(node).addClass(this.selectedClass);
20556             this.selections.push(node);
20557             if(!suppressEvent){
20558                 this.fireEvent("selectionchange", this, this.selections);
20559             }
20560         }
20561         
20562         
20563     },
20564       /**
20565      * Unselects nodes.
20566      * @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
20567      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20568      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20569      */
20570     unselect : function(nodeInfo, keepExisting, suppressEvent)
20571     {
20572         if(nodeInfo instanceof Array){
20573             Roo.each(this.selections, function(s) {
20574                 this.unselect(s, nodeInfo);
20575             }, this);
20576             return;
20577         }
20578         var node = this.getNode(nodeInfo);
20579         if(!node || !this.isSelected(node)){
20580             //Roo.log("not selected");
20581             return; // not selected.
20582         }
20583         // fireevent???
20584         var ns = [];
20585         Roo.each(this.selections, function(s) {
20586             if (s == node ) {
20587                 Roo.fly(node).removeClass(this.selectedClass);
20588
20589                 return;
20590             }
20591             ns.push(s);
20592         },this);
20593         
20594         this.selections= ns;
20595         this.fireEvent("selectionchange", this, this.selections);
20596     },
20597
20598     /**
20599      * Gets a template node.
20600      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20601      * @return {HTMLElement} The node or null if it wasn't found
20602      */
20603     getNode : function(nodeInfo){
20604         if(typeof nodeInfo == "string"){
20605             return document.getElementById(nodeInfo);
20606         }else if(typeof nodeInfo == "number"){
20607             return this.nodes[nodeInfo];
20608         }
20609         return nodeInfo;
20610     },
20611
20612     /**
20613      * Gets a range template nodes.
20614      * @param {Number} startIndex
20615      * @param {Number} endIndex
20616      * @return {Array} An array of nodes
20617      */
20618     getNodes : function(start, end){
20619         var ns = this.nodes;
20620         start = start || 0;
20621         end = typeof end == "undefined" ? ns.length - 1 : end;
20622         var nodes = [];
20623         if(start <= end){
20624             for(var i = start; i <= end; i++){
20625                 nodes.push(ns[i]);
20626             }
20627         } else{
20628             for(var i = start; i >= end; i--){
20629                 nodes.push(ns[i]);
20630             }
20631         }
20632         return nodes;
20633     },
20634
20635     /**
20636      * Finds the index of the passed node
20637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20638      * @return {Number} The index of the node or -1
20639      */
20640     indexOf : function(node){
20641         node = this.getNode(node);
20642         if(typeof node.nodeIndex == "number"){
20643             return node.nodeIndex;
20644         }
20645         var ns = this.nodes;
20646         for(var i = 0, len = ns.length; i < len; i++){
20647             if(ns[i] == node){
20648                 return i;
20649             }
20650         }
20651         return -1;
20652     }
20653 });
20654 /*
20655  * - LGPL
20656  *
20657  * based on jquery fullcalendar
20658  * 
20659  */
20660
20661 Roo.bootstrap = Roo.bootstrap || {};
20662 /**
20663  * @class Roo.bootstrap.Calendar
20664  * @extends Roo.bootstrap.Component
20665  * Bootstrap Calendar class
20666  * @cfg {Boolean} loadMask (true|false) default false
20667  * @cfg {Object} header generate the user specific header of the calendar, default false
20668
20669  * @constructor
20670  * Create a new Container
20671  * @param {Object} config The config object
20672  */
20673
20674
20675
20676 Roo.bootstrap.Calendar = function(config){
20677     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20678      this.addEvents({
20679         /**
20680              * @event select
20681              * Fires when a date is selected
20682              * @param {DatePicker} this
20683              * @param {Date} date The selected date
20684              */
20685         'select': true,
20686         /**
20687              * @event monthchange
20688              * Fires when the displayed month changes 
20689              * @param {DatePicker} this
20690              * @param {Date} date The selected month
20691              */
20692         'monthchange': true,
20693         /**
20694              * @event evententer
20695              * Fires when mouse over an event
20696              * @param {Calendar} this
20697              * @param {event} Event
20698              */
20699         'evententer': true,
20700         /**
20701              * @event eventleave
20702              * Fires when the mouse leaves an
20703              * @param {Calendar} this
20704              * @param {event}
20705              */
20706         'eventleave': true,
20707         /**
20708              * @event eventclick
20709              * Fires when the mouse click an
20710              * @param {Calendar} this
20711              * @param {event}
20712              */
20713         'eventclick': true
20714         
20715     });
20716
20717 };
20718
20719 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20720     
20721           /**
20722      * @cfg {Roo.data.Store} store
20723      * The data source for the calendar
20724      */
20725         store : false,
20726      /**
20727      * @cfg {Number} startDay
20728      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20729      */
20730     startDay : 0,
20731     
20732     loadMask : false,
20733     
20734     header : false,
20735       
20736     getAutoCreate : function(){
20737         
20738         
20739         var fc_button = function(name, corner, style, content ) {
20740             return Roo.apply({},{
20741                 tag : 'span',
20742                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20743                          (corner.length ?
20744                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20745                             ''
20746                         ),
20747                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20748                 unselectable: 'on'
20749             });
20750         };
20751         
20752         var header = {};
20753         
20754         if(!this.header){
20755             header = {
20756                 tag : 'table',
20757                 cls : 'fc-header',
20758                 style : 'width:100%',
20759                 cn : [
20760                     {
20761                         tag: 'tr',
20762                         cn : [
20763                             {
20764                                 tag : 'td',
20765                                 cls : 'fc-header-left',
20766                                 cn : [
20767                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20768                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20769                                     { tag: 'span', cls: 'fc-header-space' },
20770                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20771
20772
20773                                 ]
20774                             },
20775
20776                             {
20777                                 tag : 'td',
20778                                 cls : 'fc-header-center',
20779                                 cn : [
20780                                     {
20781                                         tag: 'span',
20782                                         cls: 'fc-header-title',
20783                                         cn : {
20784                                             tag: 'H2',
20785                                             html : 'month / year'
20786                                         }
20787                                     }
20788
20789                                 ]
20790                             },
20791                             {
20792                                 tag : 'td',
20793                                 cls : 'fc-header-right',
20794                                 cn : [
20795                               /*      fc_button('month', 'left', '', 'month' ),
20796                                     fc_button('week', '', '', 'week' ),
20797                                     fc_button('day', 'right', '', 'day' )
20798                                 */    
20799
20800                                 ]
20801                             }
20802
20803                         ]
20804                     }
20805                 ]
20806             };
20807         }
20808         
20809         header = this.header;
20810         
20811        
20812         var cal_heads = function() {
20813             var ret = [];
20814             // fixme - handle this.
20815             
20816             for (var i =0; i < Date.dayNames.length; i++) {
20817                 var d = Date.dayNames[i];
20818                 ret.push({
20819                     tag: 'th',
20820                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20821                     html : d.substring(0,3)
20822                 });
20823                 
20824             }
20825             ret[0].cls += ' fc-first';
20826             ret[6].cls += ' fc-last';
20827             return ret;
20828         };
20829         var cal_cell = function(n) {
20830             return  {
20831                 tag: 'td',
20832                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20833                 cn : [
20834                     {
20835                         cn : [
20836                             {
20837                                 cls: 'fc-day-number',
20838                                 html: 'D'
20839                             },
20840                             {
20841                                 cls: 'fc-day-content',
20842                              
20843                                 cn : [
20844                                      {
20845                                         style: 'position: relative;' // height: 17px;
20846                                     }
20847                                 ]
20848                             }
20849                             
20850                             
20851                         ]
20852                     }
20853                 ]
20854                 
20855             }
20856         };
20857         var cal_rows = function() {
20858             
20859             var ret = [];
20860             for (var r = 0; r < 6; r++) {
20861                 var row= {
20862                     tag : 'tr',
20863                     cls : 'fc-week',
20864                     cn : []
20865                 };
20866                 
20867                 for (var i =0; i < Date.dayNames.length; i++) {
20868                     var d = Date.dayNames[i];
20869                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20870
20871                 }
20872                 row.cn[0].cls+=' fc-first';
20873                 row.cn[0].cn[0].style = 'min-height:90px';
20874                 row.cn[6].cls+=' fc-last';
20875                 ret.push(row);
20876                 
20877             }
20878             ret[0].cls += ' fc-first';
20879             ret[4].cls += ' fc-prev-last';
20880             ret[5].cls += ' fc-last';
20881             return ret;
20882             
20883         };
20884         
20885         var cal_table = {
20886             tag: 'table',
20887             cls: 'fc-border-separate',
20888             style : 'width:100%',
20889             cellspacing  : 0,
20890             cn : [
20891                 { 
20892                     tag: 'thead',
20893                     cn : [
20894                         { 
20895                             tag: 'tr',
20896                             cls : 'fc-first fc-last',
20897                             cn : cal_heads()
20898                         }
20899                     ]
20900                 },
20901                 { 
20902                     tag: 'tbody',
20903                     cn : cal_rows()
20904                 }
20905                   
20906             ]
20907         };
20908          
20909          var cfg = {
20910             cls : 'fc fc-ltr',
20911             cn : [
20912                 header,
20913                 {
20914                     cls : 'fc-content',
20915                     style : "position: relative;",
20916                     cn : [
20917                         {
20918                             cls : 'fc-view fc-view-month fc-grid',
20919                             style : 'position: relative',
20920                             unselectable : 'on',
20921                             cn : [
20922                                 {
20923                                     cls : 'fc-event-container',
20924                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20925                                 },
20926                                 cal_table
20927                             ]
20928                         }
20929                     ]
20930     
20931                 }
20932            ] 
20933             
20934         };
20935         
20936          
20937         
20938         return cfg;
20939     },
20940     
20941     
20942     initEvents : function()
20943     {
20944         if(!this.store){
20945             throw "can not find store for calendar";
20946         }
20947         
20948         var mark = {
20949             tag: "div",
20950             cls:"x-dlg-mask",
20951             style: "text-align:center",
20952             cn: [
20953                 {
20954                     tag: "div",
20955                     style: "background-color:white;width:50%;margin:250 auto",
20956                     cn: [
20957                         {
20958                             tag: "img",
20959                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20960                         },
20961                         {
20962                             tag: "span",
20963                             html: "Loading"
20964                         }
20965                         
20966                     ]
20967                 }
20968             ]
20969         };
20970         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20971         
20972         var size = this.el.select('.fc-content', true).first().getSize();
20973         this.maskEl.setSize(size.width, size.height);
20974         this.maskEl.enableDisplayMode("block");
20975         if(!this.loadMask){
20976             this.maskEl.hide();
20977         }
20978         
20979         this.store = Roo.factory(this.store, Roo.data);
20980         this.store.on('load', this.onLoad, this);
20981         this.store.on('beforeload', this.onBeforeLoad, this);
20982         
20983         this.resize();
20984         
20985         this.cells = this.el.select('.fc-day',true);
20986         //Roo.log(this.cells);
20987         this.textNodes = this.el.query('.fc-day-number');
20988         this.cells.addClassOnOver('fc-state-hover');
20989         
20990         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20991         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20992         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20993         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20994         
20995         this.on('monthchange', this.onMonthChange, this);
20996         
20997         this.update(new Date().clearTime());
20998     },
20999     
21000     resize : function() {
21001         var sz  = this.el.getSize();
21002         
21003         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
21004         this.el.select('.fc-day-content div',true).setHeight(34);
21005     },
21006     
21007     
21008     // private
21009     showPrevMonth : function(e){
21010         this.update(this.activeDate.add("mo", -1));
21011     },
21012     showToday : function(e){
21013         this.update(new Date().clearTime());
21014     },
21015     // private
21016     showNextMonth : function(e){
21017         this.update(this.activeDate.add("mo", 1));
21018     },
21019
21020     // private
21021     showPrevYear : function(){
21022         this.update(this.activeDate.add("y", -1));
21023     },
21024
21025     // private
21026     showNextYear : function(){
21027         this.update(this.activeDate.add("y", 1));
21028     },
21029
21030     
21031    // private
21032     update : function(date)
21033     {
21034         var vd = this.activeDate;
21035         this.activeDate = date;
21036 //        if(vd && this.el){
21037 //            var t = date.getTime();
21038 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21039 //                Roo.log('using add remove');
21040 //                
21041 //                this.fireEvent('monthchange', this, date);
21042 //                
21043 //                this.cells.removeClass("fc-state-highlight");
21044 //                this.cells.each(function(c){
21045 //                   if(c.dateValue == t){
21046 //                       c.addClass("fc-state-highlight");
21047 //                       setTimeout(function(){
21048 //                            try{c.dom.firstChild.focus();}catch(e){}
21049 //                       }, 50);
21050 //                       return false;
21051 //                   }
21052 //                   return true;
21053 //                });
21054 //                return;
21055 //            }
21056 //        }
21057         
21058         var days = date.getDaysInMonth();
21059         
21060         var firstOfMonth = date.getFirstDateOfMonth();
21061         var startingPos = firstOfMonth.getDay()-this.startDay;
21062         
21063         if(startingPos < this.startDay){
21064             startingPos += 7;
21065         }
21066         
21067         var pm = date.add(Date.MONTH, -1);
21068         var prevStart = pm.getDaysInMonth()-startingPos;
21069 //        
21070         this.cells = this.el.select('.fc-day',true);
21071         this.textNodes = this.el.query('.fc-day-number');
21072         this.cells.addClassOnOver('fc-state-hover');
21073         
21074         var cells = this.cells.elements;
21075         var textEls = this.textNodes;
21076         
21077         Roo.each(cells, function(cell){
21078             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21079         });
21080         
21081         days += startingPos;
21082
21083         // convert everything to numbers so it's fast
21084         var day = 86400000;
21085         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21086         //Roo.log(d);
21087         //Roo.log(pm);
21088         //Roo.log(prevStart);
21089         
21090         var today = new Date().clearTime().getTime();
21091         var sel = date.clearTime().getTime();
21092         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21093         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21094         var ddMatch = this.disabledDatesRE;
21095         var ddText = this.disabledDatesText;
21096         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21097         var ddaysText = this.disabledDaysText;
21098         var format = this.format;
21099         
21100         var setCellClass = function(cal, cell){
21101             cell.row = 0;
21102             cell.events = [];
21103             cell.more = [];
21104             //Roo.log('set Cell Class');
21105             cell.title = "";
21106             var t = d.getTime();
21107             
21108             //Roo.log(d);
21109             
21110             cell.dateValue = t;
21111             if(t == today){
21112                 cell.className += " fc-today";
21113                 cell.className += " fc-state-highlight";
21114                 cell.title = cal.todayText;
21115             }
21116             if(t == sel){
21117                 // disable highlight in other month..
21118                 //cell.className += " fc-state-highlight";
21119                 
21120             }
21121             // disabling
21122             if(t < min) {
21123                 cell.className = " fc-state-disabled";
21124                 cell.title = cal.minText;
21125                 return;
21126             }
21127             if(t > max) {
21128                 cell.className = " fc-state-disabled";
21129                 cell.title = cal.maxText;
21130                 return;
21131             }
21132             if(ddays){
21133                 if(ddays.indexOf(d.getDay()) != -1){
21134                     cell.title = ddaysText;
21135                     cell.className = " fc-state-disabled";
21136                 }
21137             }
21138             if(ddMatch && format){
21139                 var fvalue = d.dateFormat(format);
21140                 if(ddMatch.test(fvalue)){
21141                     cell.title = ddText.replace("%0", fvalue);
21142                     cell.className = " fc-state-disabled";
21143                 }
21144             }
21145             
21146             if (!cell.initialClassName) {
21147                 cell.initialClassName = cell.dom.className;
21148             }
21149             
21150             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21151         };
21152
21153         var i = 0;
21154         
21155         for(; i < startingPos; i++) {
21156             textEls[i].innerHTML = (++prevStart);
21157             d.setDate(d.getDate()+1);
21158             
21159             cells[i].className = "fc-past fc-other-month";
21160             setCellClass(this, cells[i]);
21161         }
21162         
21163         var intDay = 0;
21164         
21165         for(; i < days; i++){
21166             intDay = i - startingPos + 1;
21167             textEls[i].innerHTML = (intDay);
21168             d.setDate(d.getDate()+1);
21169             
21170             cells[i].className = ''; // "x-date-active";
21171             setCellClass(this, cells[i]);
21172         }
21173         var extraDays = 0;
21174         
21175         for(; i < 42; i++) {
21176             textEls[i].innerHTML = (++extraDays);
21177             d.setDate(d.getDate()+1);
21178             
21179             cells[i].className = "fc-future fc-other-month";
21180             setCellClass(this, cells[i]);
21181         }
21182         
21183         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21184         
21185         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21186         
21187         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21188         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21189         
21190         if(totalRows != 6){
21191             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21192             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21193         }
21194         
21195         this.fireEvent('monthchange', this, date);
21196         
21197         
21198         /*
21199         if(!this.internalRender){
21200             var main = this.el.dom.firstChild;
21201             var w = main.offsetWidth;
21202             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21203             Roo.fly(main).setWidth(w);
21204             this.internalRender = true;
21205             // opera does not respect the auto grow header center column
21206             // then, after it gets a width opera refuses to recalculate
21207             // without a second pass
21208             if(Roo.isOpera && !this.secondPass){
21209                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21210                 this.secondPass = true;
21211                 this.update.defer(10, this, [date]);
21212             }
21213         }
21214         */
21215         
21216     },
21217     
21218     findCell : function(dt) {
21219         dt = dt.clearTime().getTime();
21220         var ret = false;
21221         this.cells.each(function(c){
21222             //Roo.log("check " +c.dateValue + '?=' + dt);
21223             if(c.dateValue == dt){
21224                 ret = c;
21225                 return false;
21226             }
21227             return true;
21228         });
21229         
21230         return ret;
21231     },
21232     
21233     findCells : function(ev) {
21234         var s = ev.start.clone().clearTime().getTime();
21235        // Roo.log(s);
21236         var e= ev.end.clone().clearTime().getTime();
21237        // Roo.log(e);
21238         var ret = [];
21239         this.cells.each(function(c){
21240              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21241             
21242             if(c.dateValue > e){
21243                 return ;
21244             }
21245             if(c.dateValue < s){
21246                 return ;
21247             }
21248             ret.push(c);
21249         });
21250         
21251         return ret;    
21252     },
21253     
21254 //    findBestRow: function(cells)
21255 //    {
21256 //        var ret = 0;
21257 //        
21258 //        for (var i =0 ; i < cells.length;i++) {
21259 //            ret  = Math.max(cells[i].rows || 0,ret);
21260 //        }
21261 //        return ret;
21262 //        
21263 //    },
21264     
21265     
21266     addItem : function(ev)
21267     {
21268         // look for vertical location slot in
21269         var cells = this.findCells(ev);
21270         
21271 //        ev.row = this.findBestRow(cells);
21272         
21273         // work out the location.
21274         
21275         var crow = false;
21276         var rows = [];
21277         for(var i =0; i < cells.length; i++) {
21278             
21279             cells[i].row = cells[0].row;
21280             
21281             if(i == 0){
21282                 cells[i].row = cells[i].row + 1;
21283             }
21284             
21285             if (!crow) {
21286                 crow = {
21287                     start : cells[i],
21288                     end :  cells[i]
21289                 };
21290                 continue;
21291             }
21292             if (crow.start.getY() == cells[i].getY()) {
21293                 // on same row.
21294                 crow.end = cells[i];
21295                 continue;
21296             }
21297             // different row.
21298             rows.push(crow);
21299             crow = {
21300                 start: cells[i],
21301                 end : cells[i]
21302             };
21303             
21304         }
21305         
21306         rows.push(crow);
21307         ev.els = [];
21308         ev.rows = rows;
21309         ev.cells = cells;
21310         
21311         cells[0].events.push(ev);
21312         
21313         this.calevents.push(ev);
21314     },
21315     
21316     clearEvents: function() {
21317         
21318         if(!this.calevents){
21319             return;
21320         }
21321         
21322         Roo.each(this.cells.elements, function(c){
21323             c.row = 0;
21324             c.events = [];
21325             c.more = [];
21326         });
21327         
21328         Roo.each(this.calevents, function(e) {
21329             Roo.each(e.els, function(el) {
21330                 el.un('mouseenter' ,this.onEventEnter, this);
21331                 el.un('mouseleave' ,this.onEventLeave, this);
21332                 el.remove();
21333             },this);
21334         },this);
21335         
21336         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21337             e.remove();
21338         });
21339         
21340     },
21341     
21342     renderEvents: function()
21343     {   
21344         var _this = this;
21345         
21346         this.cells.each(function(c) {
21347             
21348             if(c.row < 5){
21349                 return;
21350             }
21351             
21352             var ev = c.events;
21353             
21354             var r = 4;
21355             if(c.row != c.events.length){
21356                 r = 4 - (4 - (c.row - c.events.length));
21357             }
21358             
21359             c.events = ev.slice(0, r);
21360             c.more = ev.slice(r);
21361             
21362             if(c.more.length && c.more.length == 1){
21363                 c.events.push(c.more.pop());
21364             }
21365             
21366             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21367             
21368         });
21369             
21370         this.cells.each(function(c) {
21371             
21372             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21373             
21374             
21375             for (var e = 0; e < c.events.length; e++){
21376                 var ev = c.events[e];
21377                 var rows = ev.rows;
21378                 
21379                 for(var i = 0; i < rows.length; i++) {
21380                 
21381                     // how many rows should it span..
21382
21383                     var  cfg = {
21384                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21385                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21386
21387                         unselectable : "on",
21388                         cn : [
21389                             {
21390                                 cls: 'fc-event-inner',
21391                                 cn : [
21392     //                                {
21393     //                                  tag:'span',
21394     //                                  cls: 'fc-event-time',
21395     //                                  html : cells.length > 1 ? '' : ev.time
21396     //                                },
21397                                     {
21398                                       tag:'span',
21399                                       cls: 'fc-event-title',
21400                                       html : String.format('{0}', ev.title)
21401                                     }
21402
21403
21404                                 ]
21405                             },
21406                             {
21407                                 cls: 'ui-resizable-handle ui-resizable-e',
21408                                 html : '&nbsp;&nbsp;&nbsp'
21409                             }
21410
21411                         ]
21412                     };
21413
21414                     if (i == 0) {
21415                         cfg.cls += ' fc-event-start';
21416                     }
21417                     if ((i+1) == rows.length) {
21418                         cfg.cls += ' fc-event-end';
21419                     }
21420
21421                     var ctr = _this.el.select('.fc-event-container',true).first();
21422                     var cg = ctr.createChild(cfg);
21423
21424                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21425                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21426
21427                     var r = (c.more.length) ? 1 : 0;
21428                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21429                     cg.setWidth(ebox.right - sbox.x -2);
21430
21431                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21432                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21433                     cg.on('click', _this.onEventClick, _this, ev);
21434
21435                     ev.els.push(cg);
21436                     
21437                 }
21438                 
21439             }
21440             
21441             
21442             if(c.more.length){
21443                 var  cfg = {
21444                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21445                     style : 'position: absolute',
21446                     unselectable : "on",
21447                     cn : [
21448                         {
21449                             cls: 'fc-event-inner',
21450                             cn : [
21451                                 {
21452                                   tag:'span',
21453                                   cls: 'fc-event-title',
21454                                   html : 'More'
21455                                 }
21456
21457
21458                             ]
21459                         },
21460                         {
21461                             cls: 'ui-resizable-handle ui-resizable-e',
21462                             html : '&nbsp;&nbsp;&nbsp'
21463                         }
21464
21465                     ]
21466                 };
21467
21468                 var ctr = _this.el.select('.fc-event-container',true).first();
21469                 var cg = ctr.createChild(cfg);
21470
21471                 var sbox = c.select('.fc-day-content',true).first().getBox();
21472                 var ebox = c.select('.fc-day-content',true).first().getBox();
21473                 //Roo.log(cg);
21474                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21475                 cg.setWidth(ebox.right - sbox.x -2);
21476
21477                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21478                 
21479             }
21480             
21481         });
21482         
21483         
21484         
21485     },
21486     
21487     onEventEnter: function (e, el,event,d) {
21488         this.fireEvent('evententer', this, el, event);
21489     },
21490     
21491     onEventLeave: function (e, el,event,d) {
21492         this.fireEvent('eventleave', this, el, event);
21493     },
21494     
21495     onEventClick: function (e, el,event,d) {
21496         this.fireEvent('eventclick', this, el, event);
21497     },
21498     
21499     onMonthChange: function () {
21500         this.store.load();
21501     },
21502     
21503     onMoreEventClick: function(e, el, more)
21504     {
21505         var _this = this;
21506         
21507         this.calpopover.placement = 'right';
21508         this.calpopover.setTitle('More');
21509         
21510         this.calpopover.setContent('');
21511         
21512         var ctr = this.calpopover.el.select('.popover-content', true).first();
21513         
21514         Roo.each(more, function(m){
21515             var cfg = {
21516                 cls : 'fc-event-hori fc-event-draggable',
21517                 html : m.title
21518             };
21519             var cg = ctr.createChild(cfg);
21520             
21521             cg.on('click', _this.onEventClick, _this, m);
21522         });
21523         
21524         this.calpopover.show(el);
21525         
21526         
21527     },
21528     
21529     onLoad: function () 
21530     {   
21531         this.calevents = [];
21532         var cal = this;
21533         
21534         if(this.store.getCount() > 0){
21535             this.store.data.each(function(d){
21536                cal.addItem({
21537                     id : d.data.id,
21538                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21539                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21540                     time : d.data.start_time,
21541                     title : d.data.title,
21542                     description : d.data.description,
21543                     venue : d.data.venue
21544                 });
21545             });
21546         }
21547         
21548         this.renderEvents();
21549         
21550         if(this.calevents.length && this.loadMask){
21551             this.maskEl.hide();
21552         }
21553     },
21554     
21555     onBeforeLoad: function()
21556     {
21557         this.clearEvents();
21558         if(this.loadMask){
21559             this.maskEl.show();
21560         }
21561     }
21562 });
21563
21564  
21565  /*
21566  * - LGPL
21567  *
21568  * element
21569  * 
21570  */
21571
21572 /**
21573  * @class Roo.bootstrap.Popover
21574  * @extends Roo.bootstrap.Component
21575  * @parent none builder
21576  * @children Roo.bootstrap.Component
21577  * Bootstrap Popover class
21578  * @cfg {String} html contents of the popover   (or false to use children..)
21579  * @cfg {String} title of popover (or false to hide)
21580  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21581  * @cfg {String} trigger click || hover (or false to trigger manually)
21582  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21583  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21584  *      - if false and it has a 'parent' then it will be automatically added to that element
21585  *      - if string - Roo.get  will be called 
21586  * @cfg {Number} delay - delay before showing
21587  
21588  * @constructor
21589  * Create a new Popover
21590  * @param {Object} config The config object
21591  */
21592
21593 Roo.bootstrap.Popover = function(config){
21594     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21595     
21596     this.addEvents({
21597         // raw events
21598          /**
21599          * @event show
21600          * After the popover show
21601          * 
21602          * @param {Roo.bootstrap.Popover} this
21603          */
21604         "show" : true,
21605         /**
21606          * @event hide
21607          * After the popover hide
21608          * 
21609          * @param {Roo.bootstrap.Popover} this
21610          */
21611         "hide" : true
21612     });
21613 };
21614
21615 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21616     
21617     title: false,
21618     html: false,
21619     
21620     placement : 'right',
21621     trigger : 'hover', // hover
21622     modal : false,
21623     delay : 0,
21624     
21625     over: false,
21626     
21627     can_build_overlaid : false,
21628     
21629     maskEl : false, // the mask element
21630     headerEl : false,
21631     contentEl : false,
21632     alignEl : false, // when show is called with an element - this get's stored.
21633     
21634     getChildContainer : function()
21635     {
21636         return this.contentEl;
21637         
21638     },
21639     getPopoverHeader : function()
21640     {
21641         this.title = true; // flag not to hide it..
21642         this.headerEl.addClass('p-0');
21643         return this.headerEl
21644     },
21645     
21646     
21647     getAutoCreate : function(){
21648          
21649         var cfg = {
21650            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21651            style: 'display:block',
21652            cn : [
21653                 {
21654                     cls : 'arrow'
21655                 },
21656                 {
21657                     cls : 'popover-inner ',
21658                     cn : [
21659                         {
21660                             tag: 'h3',
21661                             cls: 'popover-title popover-header',
21662                             html : this.title === false ? '' : this.title
21663                         },
21664                         {
21665                             cls : 'popover-content popover-body '  + (this.cls || ''),
21666                             html : this.html || ''
21667                         }
21668                     ]
21669                     
21670                 }
21671            ]
21672         };
21673         
21674         return cfg;
21675     },
21676     /**
21677      * @param {string} the title
21678      */
21679     setTitle: function(str)
21680     {
21681         this.title = str;
21682         if (this.el) {
21683             this.headerEl.dom.innerHTML = str;
21684         }
21685         
21686     },
21687     /**
21688      * @param {string} the body content
21689      */
21690     setContent: function(str)
21691     {
21692         this.html = str;
21693         if (this.contentEl) {
21694             this.contentEl.dom.innerHTML = str;
21695         }
21696         
21697     },
21698     // as it get's added to the bottom of the page.
21699     onRender : function(ct, position)
21700     {
21701         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21702         
21703         
21704         
21705         if(!this.el){
21706             var cfg = Roo.apply({},  this.getAutoCreate());
21707             cfg.id = Roo.id();
21708             
21709             if (this.cls) {
21710                 cfg.cls += ' ' + this.cls;
21711             }
21712             if (this.style) {
21713                 cfg.style = this.style;
21714             }
21715             //Roo.log("adding to ");
21716             this.el = Roo.get(document.body).createChild(cfg, position);
21717 //            Roo.log(this.el);
21718         }
21719         
21720         this.contentEl = this.el.select('.popover-content',true).first();
21721         this.headerEl =  this.el.select('.popover-title',true).first();
21722         
21723         var nitems = [];
21724         if(typeof(this.items) != 'undefined'){
21725             var items = this.items;
21726             delete this.items;
21727
21728             for(var i =0;i < items.length;i++) {
21729                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21730             }
21731         }
21732
21733         this.items = nitems;
21734         
21735         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21736         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21737         
21738         
21739         
21740         this.initEvents();
21741     },
21742     
21743     resizeMask : function()
21744     {
21745         this.maskEl.setSize(
21746             Roo.lib.Dom.getViewWidth(true),
21747             Roo.lib.Dom.getViewHeight(true)
21748         );
21749     },
21750     
21751     initEvents : function()
21752     {
21753         
21754         if (!this.modal) { 
21755             Roo.bootstrap.Popover.register(this);
21756         }
21757          
21758         this.arrowEl = this.el.select('.arrow',true).first();
21759         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21760         this.el.enableDisplayMode('block');
21761         this.el.hide();
21762  
21763         
21764         if (this.over === false && !this.parent()) {
21765             return; 
21766         }
21767         if (this.triggers === false) {
21768             return;
21769         }
21770          
21771         // support parent
21772         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21773         var triggers = this.trigger ? this.trigger.split(' ') : [];
21774         Roo.each(triggers, function(trigger) {
21775         
21776             if (trigger == 'click') {
21777                 on_el.on('click', this.toggle, this);
21778             } else if (trigger != 'manual') {
21779                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21780                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21781       
21782                 on_el.on(eventIn  ,this.enter, this);
21783                 on_el.on(eventOut, this.leave, this);
21784             }
21785         }, this);
21786     },
21787     
21788     
21789     // private
21790     timeout : null,
21791     hoverState : null,
21792     
21793     toggle : function () {
21794         this.hoverState == 'in' ? this.leave() : this.enter();
21795     },
21796     
21797     enter : function () {
21798         
21799         clearTimeout(this.timeout);
21800     
21801         this.hoverState = 'in';
21802     
21803         if (!this.delay || !this.delay.show) {
21804             this.show();
21805             return;
21806         }
21807         var _t = this;
21808         this.timeout = setTimeout(function () {
21809             if (_t.hoverState == 'in') {
21810                 _t.show();
21811             }
21812         }, this.delay.show)
21813     },
21814     
21815     leave : function() {
21816         clearTimeout(this.timeout);
21817     
21818         this.hoverState = 'out';
21819     
21820         if (!this.delay || !this.delay.hide) {
21821             this.hide();
21822             return;
21823         }
21824         var _t = this;
21825         this.timeout = setTimeout(function () {
21826             if (_t.hoverState == 'out') {
21827                 _t.hide();
21828             }
21829         }, this.delay.hide)
21830     },
21831     
21832     /**
21833      * update the position of the dialog
21834      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21835      * 
21836      *
21837      */
21838     
21839     doAlign : function()
21840     {
21841         
21842         if (this.alignEl) {
21843             this.updatePosition(this.placement, true);
21844              
21845         } else {
21846             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21847             var es = this.el.getSize();
21848             var x = Roo.lib.Dom.getViewWidth()/2;
21849             var y = Roo.lib.Dom.getViewHeight()/2;
21850             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21851             
21852         }
21853
21854          
21855          
21856         
21857         
21858     },
21859     
21860     /**
21861      * Show the popover
21862      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21863      * @param {string} (left|right|top|bottom) position
21864      */
21865     show : function (on_el, placement)
21866     {
21867         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21868         on_el = on_el || false; // default to false
21869          
21870         if (!on_el) {
21871             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21872                 on_el = this.parent().el;
21873             } else if (this.over) {
21874                 on_el = Roo.get(this.over);
21875             }
21876             
21877         }
21878         
21879         this.alignEl = Roo.get( on_el );
21880
21881         if (!this.el) {
21882             this.render(document.body);
21883         }
21884         
21885         
21886          
21887         
21888         if (this.title === false) {
21889             this.headerEl.hide();
21890         }
21891         
21892        
21893         this.el.show();
21894         this.el.dom.style.display = 'block';
21895          
21896         this.doAlign();
21897         
21898         //var arrow = this.el.select('.arrow',true).first();
21899         //arrow.set(align[2], 
21900         
21901         this.el.addClass('in');
21902         
21903          
21904         
21905         this.hoverState = 'in';
21906         
21907         if (this.modal) {
21908             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21909             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21910             this.maskEl.dom.style.display = 'block';
21911             this.maskEl.addClass('show');
21912         }
21913         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21914  
21915         this.fireEvent('show', this);
21916         
21917     },
21918     /**
21919      * fire this manually after loading a grid in the table for example
21920      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21921      * @param {Boolean} try and move it if we cant get right position.
21922      */
21923     updatePosition : function(placement, try_move)
21924     {
21925         // allow for calling with no parameters
21926         placement = placement   ? placement :  this.placement;
21927         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21928         
21929         this.el.removeClass([
21930             'fade','top','bottom', 'left', 'right','in',
21931             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21932         ]);
21933         this.el.addClass(placement + ' bs-popover-' + placement);
21934         
21935         if (!this.alignEl ) {
21936             return false;
21937         }
21938         
21939         switch (placement) {
21940             case 'right':
21941                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21942                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21943                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21944                     //normal display... or moved up/down.
21945                     this.el.setXY(offset);
21946                     var xy = this.alignEl.getAnchorXY('tr', false);
21947                     xy[0]+=2;xy[1]+=5;
21948                     this.arrowEl.setXY(xy);
21949                     return true;
21950                 }
21951                 // continue through...
21952                 return this.updatePosition('left', false);
21953                 
21954             
21955             case 'left':
21956                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21957                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21958                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21959                     //normal display... or moved up/down.
21960                     this.el.setXY(offset);
21961                     var xy = this.alignEl.getAnchorXY('tl', false);
21962                     xy[0]-=10;xy[1]+=5; // << fix me
21963                     this.arrowEl.setXY(xy);
21964                     return true;
21965                 }
21966                 // call self...
21967                 return this.updatePosition('right', false);
21968             
21969             case 'top':
21970                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21971                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21972                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21973                     //normal display... or moved up/down.
21974                     this.el.setXY(offset);
21975                     var xy = this.alignEl.getAnchorXY('t', false);
21976                     xy[1]-=10; // << fix me
21977                     this.arrowEl.setXY(xy);
21978                     return true;
21979                 }
21980                 // fall through
21981                return this.updatePosition('bottom', false);
21982             
21983             case 'bottom':
21984                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21985                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21986                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21987                     //normal display... or moved up/down.
21988                     this.el.setXY(offset);
21989                     var xy = this.alignEl.getAnchorXY('b', false);
21990                      xy[1]+=2; // << fix me
21991                     this.arrowEl.setXY(xy);
21992                     return true;
21993                 }
21994                 // fall through
21995                 return this.updatePosition('top', false);
21996                 
21997             
21998         }
21999         
22000         
22001         return false;
22002     },
22003     
22004     hide : function()
22005     {
22006         this.el.setXY([0,0]);
22007         this.el.removeClass('in');
22008         this.el.hide();
22009         this.hoverState = null;
22010         this.maskEl.hide(); // always..
22011         this.fireEvent('hide', this);
22012     }
22013     
22014 });
22015
22016
22017 Roo.apply(Roo.bootstrap.Popover, {
22018
22019     alignment : {
22020         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22021         'right' : ['l-br', [10,0], 'right bs-popover-right'],
22022         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22023         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22024     },
22025     
22026     zIndex : 20001,
22027
22028     clickHander : false,
22029     
22030     
22031
22032     onMouseDown : function(e)
22033     {
22034         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22035             /// what is nothing is showing..
22036             this.hideAll();
22037         }
22038          
22039     },
22040     
22041     
22042     popups : [],
22043     
22044     register : function(popup)
22045     {
22046         if (!Roo.bootstrap.Popover.clickHandler) {
22047             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22048         }
22049         // hide other popups.
22050         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22051         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22052         this.hideAll(); //<< why?
22053         //this.popups.push(popup);
22054     },
22055     hideAll : function()
22056     {
22057         this.popups.forEach(function(p) {
22058             p.hide();
22059         });
22060     },
22061     onShow : function() {
22062         Roo.bootstrap.Popover.popups.push(this);
22063     },
22064     onHide : function() {
22065         Roo.bootstrap.Popover.popups.remove(this);
22066     } 
22067
22068 });
22069 /**
22070  * @class Roo.bootstrap.PopoverNav
22071  * @extends Roo.bootstrap.nav.Simplebar
22072  * @parent Roo.bootstrap.Popover
22073  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22074  * @licence LGPL
22075  * Bootstrap Popover header navigation class
22076  * FIXME? should this go under nav?
22077  *
22078  * 
22079  * @constructor
22080  * Create a new Popover Header Navigation 
22081  * @param {Object} config The config object
22082  */
22083
22084 Roo.bootstrap.PopoverNav = function(config){
22085     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22086 };
22087
22088 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22089     
22090     
22091     container_method : 'getPopoverHeader' 
22092     
22093      
22094     
22095     
22096    
22097 });
22098
22099  
22100
22101  /*
22102  * - LGPL
22103  *
22104  * Progress
22105  * 
22106  */
22107
22108 /**
22109  * @class Roo.bootstrap.Progress
22110  * @extends Roo.bootstrap.Component
22111  * @children Roo.bootstrap.ProgressBar
22112  * Bootstrap Progress class
22113  * @cfg {Boolean} striped striped of the progress bar
22114  * @cfg {Boolean} active animated of the progress bar
22115  * 
22116  * 
22117  * @constructor
22118  * Create a new Progress
22119  * @param {Object} config The config object
22120  */
22121
22122 Roo.bootstrap.Progress = function(config){
22123     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22124 };
22125
22126 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22127     
22128     striped : false,
22129     active: false,
22130     
22131     getAutoCreate : function(){
22132         var cfg = {
22133             tag: 'div',
22134             cls: 'progress'
22135         };
22136         
22137         
22138         if(this.striped){
22139             cfg.cls += ' progress-striped';
22140         }
22141       
22142         if(this.active){
22143             cfg.cls += ' active';
22144         }
22145         
22146         
22147         return cfg;
22148     }
22149    
22150 });
22151
22152  
22153
22154  /*
22155  * - LGPL
22156  *
22157  * ProgressBar
22158  * 
22159  */
22160
22161 /**
22162  * @class Roo.bootstrap.ProgressBar
22163  * @extends Roo.bootstrap.Component
22164  * Bootstrap ProgressBar class
22165  * @cfg {Number} aria_valuenow aria-value now
22166  * @cfg {Number} aria_valuemin aria-value min
22167  * @cfg {Number} aria_valuemax aria-value max
22168  * @cfg {String} label label for the progress bar
22169  * @cfg {String} panel (success | info | warning | danger )
22170  * @cfg {String} role role of the progress bar
22171  * @cfg {String} sr_only text
22172  * 
22173  * 
22174  * @constructor
22175  * Create a new ProgressBar
22176  * @param {Object} config The config object
22177  */
22178
22179 Roo.bootstrap.ProgressBar = function(config){
22180     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22181 };
22182
22183 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22184     
22185     aria_valuenow : 0,
22186     aria_valuemin : 0,
22187     aria_valuemax : 100,
22188     label : false,
22189     panel : false,
22190     role : false,
22191     sr_only: false,
22192     
22193     getAutoCreate : function()
22194     {
22195         
22196         var cfg = {
22197             tag: 'div',
22198             cls: 'progress-bar',
22199             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22200         };
22201         
22202         if(this.sr_only){
22203             cfg.cn = {
22204                 tag: 'span',
22205                 cls: 'sr-only',
22206                 html: this.sr_only
22207             }
22208         }
22209         
22210         if(this.role){
22211             cfg.role = this.role;
22212         }
22213         
22214         if(this.aria_valuenow){
22215             cfg['aria-valuenow'] = this.aria_valuenow;
22216         }
22217         
22218         if(this.aria_valuemin){
22219             cfg['aria-valuemin'] = this.aria_valuemin;
22220         }
22221         
22222         if(this.aria_valuemax){
22223             cfg['aria-valuemax'] = this.aria_valuemax;
22224         }
22225         
22226         if(this.label && !this.sr_only){
22227             cfg.html = this.label;
22228         }
22229         
22230         if(this.panel){
22231             cfg.cls += ' progress-bar-' + this.panel;
22232         }
22233         
22234         return cfg;
22235     },
22236     
22237     update : function(aria_valuenow)
22238     {
22239         this.aria_valuenow = aria_valuenow;
22240         
22241         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22242     }
22243    
22244 });
22245
22246  
22247
22248  /**
22249  * @class Roo.bootstrap.TabGroup
22250  * @extends Roo.bootstrap.Column
22251  * @children Roo.bootstrap.TabPanel
22252  * Bootstrap Column class
22253  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22254  * @cfg {Boolean} carousel true to make the group behave like a carousel
22255  * @cfg {Boolean} bullets show bullets for the panels
22256  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22257  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22258  * @cfg {Boolean} showarrow (true|false) show arrow default true
22259  * 
22260  * @constructor
22261  * Create a new TabGroup
22262  * @param {Object} config The config object
22263  */
22264
22265 Roo.bootstrap.TabGroup = function(config){
22266     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22267     if (!this.navId) {
22268         this.navId = Roo.id();
22269     }
22270     this.tabs = [];
22271     Roo.bootstrap.TabGroup.register(this);
22272     
22273 };
22274
22275 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22276     
22277     carousel : false,
22278     transition : false,
22279     bullets : 0,
22280     timer : 0,
22281     autoslide : false,
22282     slideFn : false,
22283     slideOnTouch : false,
22284     showarrow : true,
22285     
22286     getAutoCreate : function()
22287     {
22288         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22289         
22290         cfg.cls += ' tab-content';
22291         
22292         if (this.carousel) {
22293             cfg.cls += ' carousel slide';
22294             
22295             cfg.cn = [{
22296                cls : 'carousel-inner',
22297                cn : []
22298             }];
22299         
22300             if(this.bullets  && !Roo.isTouch){
22301                 
22302                 var bullets = {
22303                     cls : 'carousel-bullets',
22304                     cn : []
22305                 };
22306                
22307                 if(this.bullets_cls){
22308                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22309                 }
22310                 
22311                 bullets.cn.push({
22312                     cls : 'clear'
22313                 });
22314                 
22315                 cfg.cn[0].cn.push(bullets);
22316             }
22317             
22318             if(this.showarrow){
22319                 cfg.cn[0].cn.push({
22320                     tag : 'div',
22321                     class : 'carousel-arrow',
22322                     cn : [
22323                         {
22324                             tag : 'div',
22325                             class : 'carousel-prev',
22326                             cn : [
22327                                 {
22328                                     tag : 'i',
22329                                     class : 'fa fa-chevron-left'
22330                                 }
22331                             ]
22332                         },
22333                         {
22334                             tag : 'div',
22335                             class : 'carousel-next',
22336                             cn : [
22337                                 {
22338                                     tag : 'i',
22339                                     class : 'fa fa-chevron-right'
22340                                 }
22341                             ]
22342                         }
22343                     ]
22344                 });
22345             }
22346             
22347         }
22348         
22349         return cfg;
22350     },
22351     
22352     initEvents:  function()
22353     {
22354 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22355 //            this.el.on("touchstart", this.onTouchStart, this);
22356 //        }
22357         
22358         if(this.autoslide){
22359             var _this = this;
22360             
22361             this.slideFn = window.setInterval(function() {
22362                 _this.showPanelNext();
22363             }, this.timer);
22364         }
22365         
22366         if(this.showarrow){
22367             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22368             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22369         }
22370         
22371         
22372     },
22373     
22374 //    onTouchStart : function(e, el, o)
22375 //    {
22376 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22377 //            return;
22378 //        }
22379 //        
22380 //        this.showPanelNext();
22381 //    },
22382     
22383     
22384     getChildContainer : function()
22385     {
22386         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22387     },
22388     
22389     /**
22390     * register a Navigation item
22391     * @param {Roo.bootstrap.nav.Item} the navitem to add
22392     */
22393     register : function(item)
22394     {
22395         this.tabs.push( item);
22396         item.navId = this.navId; // not really needed..
22397         this.addBullet();
22398     
22399     },
22400     
22401     getActivePanel : function()
22402     {
22403         var r = false;
22404         Roo.each(this.tabs, function(t) {
22405             if (t.active) {
22406                 r = t;
22407                 return false;
22408             }
22409             return null;
22410         });
22411         return r;
22412         
22413     },
22414     getPanelByName : function(n)
22415     {
22416         var r = false;
22417         Roo.each(this.tabs, function(t) {
22418             if (t.tabId == n) {
22419                 r = t;
22420                 return false;
22421             }
22422             return null;
22423         });
22424         return r;
22425     },
22426     indexOfPanel : function(p)
22427     {
22428         var r = false;
22429         Roo.each(this.tabs, function(t,i) {
22430             if (t.tabId == p.tabId) {
22431                 r = i;
22432                 return false;
22433             }
22434             return null;
22435         });
22436         return r;
22437     },
22438     /**
22439      * show a specific panel
22440      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22441      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22442      */
22443     showPanel : function (pan)
22444     {
22445         if(this.transition || typeof(pan) == 'undefined'){
22446             Roo.log("waiting for the transitionend");
22447             return false;
22448         }
22449         
22450         if (typeof(pan) == 'number') {
22451             pan = this.tabs[pan];
22452         }
22453         
22454         if (typeof(pan) == 'string') {
22455             pan = this.getPanelByName(pan);
22456         }
22457         
22458         var cur = this.getActivePanel();
22459         
22460         if(!pan || !cur){
22461             Roo.log('pan or acitve pan is undefined');
22462             return false;
22463         }
22464         
22465         if (pan.tabId == this.getActivePanel().tabId) {
22466             return true;
22467         }
22468         
22469         if (false === cur.fireEvent('beforedeactivate')) {
22470             return false;
22471         }
22472         
22473         if(this.bullets > 0 && !Roo.isTouch){
22474             this.setActiveBullet(this.indexOfPanel(pan));
22475         }
22476         
22477         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22478             
22479             //class="carousel-item carousel-item-next carousel-item-left"
22480             
22481             this.transition = true;
22482             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22483             var lr = dir == 'next' ? 'left' : 'right';
22484             pan.el.addClass(dir); // or prev
22485             pan.el.addClass('carousel-item-' + dir); // or prev
22486             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22487             cur.el.addClass(lr); // or right
22488             pan.el.addClass(lr);
22489             cur.el.addClass('carousel-item-' +lr); // or right
22490             pan.el.addClass('carousel-item-' +lr);
22491             
22492             
22493             var _this = this;
22494             cur.el.on('transitionend', function() {
22495                 Roo.log("trans end?");
22496                 
22497                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22498                 pan.setActive(true);
22499                 
22500                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22501                 cur.setActive(false);
22502                 
22503                 _this.transition = false;
22504                 
22505             }, this, { single:  true } );
22506             
22507             return true;
22508         }
22509         
22510         cur.setActive(false);
22511         pan.setActive(true);
22512         
22513         return true;
22514         
22515     },
22516     showPanelNext : function()
22517     {
22518         var i = this.indexOfPanel(this.getActivePanel());
22519         
22520         if (i >= this.tabs.length - 1 && !this.autoslide) {
22521             return;
22522         }
22523         
22524         if (i >= this.tabs.length - 1 && this.autoslide) {
22525             i = -1;
22526         }
22527         
22528         this.showPanel(this.tabs[i+1]);
22529     },
22530     
22531     showPanelPrev : function()
22532     {
22533         var i = this.indexOfPanel(this.getActivePanel());
22534         
22535         if (i  < 1 && !this.autoslide) {
22536             return;
22537         }
22538         
22539         if (i < 1 && this.autoslide) {
22540             i = this.tabs.length;
22541         }
22542         
22543         this.showPanel(this.tabs[i-1]);
22544     },
22545     
22546     
22547     addBullet: function()
22548     {
22549         if(!this.bullets || Roo.isTouch){
22550             return;
22551         }
22552         var ctr = this.el.select('.carousel-bullets',true).first();
22553         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22554         var bullet = ctr.createChild({
22555             cls : 'bullet bullet-' + i
22556         },ctr.dom.lastChild);
22557         
22558         
22559         var _this = this;
22560         
22561         bullet.on('click', (function(e, el, o, ii, t){
22562
22563             e.preventDefault();
22564
22565             this.showPanel(ii);
22566
22567             if(this.autoslide && this.slideFn){
22568                 clearInterval(this.slideFn);
22569                 this.slideFn = window.setInterval(function() {
22570                     _this.showPanelNext();
22571                 }, this.timer);
22572             }
22573
22574         }).createDelegate(this, [i, bullet], true));
22575                 
22576         
22577     },
22578      
22579     setActiveBullet : function(i)
22580     {
22581         if(Roo.isTouch){
22582             return;
22583         }
22584         
22585         Roo.each(this.el.select('.bullet', true).elements, function(el){
22586             el.removeClass('selected');
22587         });
22588
22589         var bullet = this.el.select('.bullet-' + i, true).first();
22590         
22591         if(!bullet){
22592             return;
22593         }
22594         
22595         bullet.addClass('selected');
22596     }
22597     
22598     
22599   
22600 });
22601
22602  
22603
22604  
22605  
22606 Roo.apply(Roo.bootstrap.TabGroup, {
22607     
22608     groups: {},
22609      /**
22610     * register a Navigation Group
22611     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22612     */
22613     register : function(navgrp)
22614     {
22615         this.groups[navgrp.navId] = navgrp;
22616         
22617     },
22618     /**
22619     * fetch a Navigation Group based on the navigation ID
22620     * if one does not exist , it will get created.
22621     * @param {string} the navgroup to add
22622     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22623     */
22624     get: function(navId) {
22625         if (typeof(this.groups[navId]) == 'undefined') {
22626             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22627         }
22628         return this.groups[navId] ;
22629     }
22630     
22631     
22632     
22633 });
22634
22635  /*
22636  * - LGPL
22637  *
22638  * TabPanel
22639  * 
22640  */
22641
22642 /**
22643  * @class Roo.bootstrap.TabPanel
22644  * @extends Roo.bootstrap.Component
22645  * @children Roo.bootstrap.Component
22646  * Bootstrap TabPanel class
22647  * @cfg {Boolean} active panel active
22648  * @cfg {String} html panel content
22649  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22650  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22651  * @cfg {String} href click to link..
22652  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22653  * 
22654  * 
22655  * @constructor
22656  * Create a new TabPanel
22657  * @param {Object} config The config object
22658  */
22659
22660 Roo.bootstrap.TabPanel = function(config){
22661     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22662     this.addEvents({
22663         /**
22664              * @event changed
22665              * Fires when the active status changes
22666              * @param {Roo.bootstrap.TabPanel} this
22667              * @param {Boolean} state the new state
22668             
22669          */
22670         'changed': true,
22671         /**
22672              * @event beforedeactivate
22673              * Fires before a tab is de-activated - can be used to do validation on a form.
22674              * @param {Roo.bootstrap.TabPanel} this
22675              * @return {Boolean} false if there is an error
22676             
22677          */
22678         'beforedeactivate': true
22679      });
22680     
22681     this.tabId = this.tabId || Roo.id();
22682   
22683 };
22684
22685 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22686     
22687     active: false,
22688     html: false,
22689     tabId: false,
22690     navId : false,
22691     href : '',
22692     touchSlide : false,
22693     getAutoCreate : function(){
22694         
22695         
22696         var cfg = {
22697             tag: 'div',
22698             // item is needed for carousel - not sure if it has any effect otherwise
22699             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22700             html: this.html || ''
22701         };
22702         
22703         if(this.active){
22704             cfg.cls += ' active';
22705         }
22706         
22707         if(this.tabId){
22708             cfg.tabId = this.tabId;
22709         }
22710         
22711         
22712         
22713         return cfg;
22714     },
22715     
22716     initEvents:  function()
22717     {
22718         var p = this.parent();
22719         
22720         this.navId = this.navId || p.navId;
22721         
22722         if (typeof(this.navId) != 'undefined') {
22723             // not really needed.. but just in case.. parent should be a NavGroup.
22724             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22725             
22726             tg.register(this);
22727             
22728             var i = tg.tabs.length - 1;
22729             
22730             if(this.active && tg.bullets > 0 && i < tg.bullets){
22731                 tg.setActiveBullet(i);
22732             }
22733         }
22734         
22735         this.el.on('click', this.onClick, this);
22736         
22737         if(Roo.isTouch && this.touchSlide){
22738             this.el.on("touchstart", this.onTouchStart, this);
22739             this.el.on("touchmove", this.onTouchMove, this);
22740             this.el.on("touchend", this.onTouchEnd, this);
22741         }
22742         
22743     },
22744     
22745     onRender : function(ct, position)
22746     {
22747         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22748     },
22749     
22750     setActive : function(state)
22751     {
22752         Roo.log("panel - set active " + this.tabId + "=" + state);
22753         
22754         this.active = state;
22755         if (!state) {
22756             this.el.removeClass('active');
22757             
22758         } else  if (!this.el.hasClass('active')) {
22759             this.el.addClass('active');
22760         }
22761         
22762         this.fireEvent('changed', this, state);
22763     },
22764     
22765     onClick : function(e)
22766     {
22767         e.preventDefault();
22768         
22769         if(!this.href.length){
22770             return;
22771         }
22772         
22773         window.location.href = this.href;
22774     },
22775     
22776     startX : 0,
22777     startY : 0,
22778     endX : 0,
22779     endY : 0,
22780     swiping : false,
22781     
22782     onTouchStart : function(e)
22783     {
22784         this.swiping = false;
22785         
22786         this.startX = e.browserEvent.touches[0].clientX;
22787         this.startY = e.browserEvent.touches[0].clientY;
22788     },
22789     
22790     onTouchMove : function(e)
22791     {
22792         this.swiping = true;
22793         
22794         this.endX = e.browserEvent.touches[0].clientX;
22795         this.endY = e.browserEvent.touches[0].clientY;
22796     },
22797     
22798     onTouchEnd : function(e)
22799     {
22800         if(!this.swiping){
22801             this.onClick(e);
22802             return;
22803         }
22804         
22805         var tabGroup = this.parent();
22806         
22807         if(this.endX > this.startX){ // swiping right
22808             tabGroup.showPanelPrev();
22809             return;
22810         }
22811         
22812         if(this.startX > this.endX){ // swiping left
22813             tabGroup.showPanelNext();
22814             return;
22815         }
22816     }
22817     
22818     
22819 });
22820  
22821
22822  
22823
22824  /*
22825  * - LGPL
22826  *
22827  * DateField
22828  * 
22829  */
22830
22831 /**
22832  * @class Roo.bootstrap.form.DateField
22833  * @extends Roo.bootstrap.form.Input
22834  * Bootstrap DateField class
22835  * @cfg {Number} weekStart default 0
22836  * @cfg {String} viewMode default empty, (months|years)
22837  * @cfg {String} minViewMode default empty, (months|years)
22838  * @cfg {Number} startDate default -Infinity
22839  * @cfg {Number} endDate default Infinity
22840  * @cfg {Boolean} todayHighlight default false
22841  * @cfg {Boolean} todayBtn default false
22842  * @cfg {Boolean} calendarWeeks default false
22843  * @cfg {Object} daysOfWeekDisabled default empty
22844  * @cfg {Boolean} singleMode default false (true | false)
22845  * 
22846  * @cfg {Boolean} keyboardNavigation default true
22847  * @cfg {String} language default en
22848  * 
22849  * @constructor
22850  * Create a new DateField
22851  * @param {Object} config The config object
22852  */
22853
22854 Roo.bootstrap.form.DateField = function(config){
22855     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22856      this.addEvents({
22857             /**
22858              * @event show
22859              * Fires when this field show.
22860              * @param {Roo.bootstrap.form.DateField} this
22861              * @param {Mixed} date The date value
22862              */
22863             show : true,
22864             /**
22865              * @event show
22866              * Fires when this field hide.
22867              * @param {Roo.bootstrap.form.DateField} this
22868              * @param {Mixed} date The date value
22869              */
22870             hide : true,
22871             /**
22872              * @event select
22873              * Fires when select a date.
22874              * @param {Roo.bootstrap.form.DateField} this
22875              * @param {Mixed} date The date value
22876              */
22877             select : true,
22878             /**
22879              * @event beforeselect
22880              * Fires when before select a date.
22881              * @param {Roo.bootstrap.form.DateField} this
22882              * @param {Mixed} date The date value
22883              */
22884             beforeselect : true
22885         });
22886 };
22887
22888 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22889     
22890     /**
22891      * @cfg {String} format
22892      * The default date format string which can be overriden for localization support.  The format must be
22893      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22894      */
22895     format : "m/d/y",
22896     /**
22897      * @cfg {String} altFormats
22898      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22899      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22900      */
22901     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22902     
22903     weekStart : 0,
22904     
22905     viewMode : '',
22906     
22907     minViewMode : '',
22908     
22909     todayHighlight : false,
22910     
22911     todayBtn: false,
22912     
22913     language: 'en',
22914     
22915     keyboardNavigation: true,
22916     
22917     calendarWeeks: false,
22918     
22919     startDate: -Infinity,
22920     
22921     endDate: Infinity,
22922     
22923     daysOfWeekDisabled: [],
22924     
22925     _events: [],
22926     
22927     singleMode : false,
22928     
22929     UTCDate: function()
22930     {
22931         return new Date(Date.UTC.apply(Date, arguments));
22932     },
22933     
22934     UTCToday: function()
22935     {
22936         var today = new Date();
22937         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22938     },
22939     
22940     getDate: function() {
22941             var d = this.getUTCDate();
22942             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22943     },
22944     
22945     getUTCDate: function() {
22946             return this.date;
22947     },
22948     
22949     setDate: function(d) {
22950             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22951     },
22952     
22953     setUTCDate: function(d) {
22954             this.date = d;
22955             this.setValue(this.formatDate(this.date));
22956     },
22957         
22958     onRender: function(ct, position)
22959     {
22960         
22961         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22962         
22963         this.language = this.language || 'en';
22964         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22965         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22966         
22967         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22968         this.format = this.format || 'm/d/y';
22969         this.isInline = false;
22970         this.isInput = true;
22971         this.component = this.el.select('.add-on', true).first() || false;
22972         this.component = (this.component && this.component.length === 0) ? false : this.component;
22973         this.hasInput = this.component && this.inputEl().length;
22974         
22975         if (typeof(this.minViewMode === 'string')) {
22976             switch (this.minViewMode) {
22977                 case 'months':
22978                     this.minViewMode = 1;
22979                     break;
22980                 case 'years':
22981                     this.minViewMode = 2;
22982                     break;
22983                 default:
22984                     this.minViewMode = 0;
22985                     break;
22986             }
22987         }
22988         
22989         if (typeof(this.viewMode === 'string')) {
22990             switch (this.viewMode) {
22991                 case 'months':
22992                     this.viewMode = 1;
22993                     break;
22994                 case 'years':
22995                     this.viewMode = 2;
22996                     break;
22997                 default:
22998                     this.viewMode = 0;
22999                     break;
23000             }
23001         }
23002                 
23003         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
23004         
23005 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
23006         
23007         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23008         
23009         this.picker().on('mousedown', this.onMousedown, this);
23010         this.picker().on('click', this.onClick, this);
23011         
23012         this.picker().addClass('datepicker-dropdown');
23013         
23014         this.startViewMode = this.viewMode;
23015         
23016         if(this.singleMode){
23017             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
23018                 v.setVisibilityMode(Roo.Element.DISPLAY);
23019                 v.hide();
23020             });
23021             
23022             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23023                 v.setStyle('width', '189px');
23024             });
23025         }
23026         
23027         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23028             if(!this.calendarWeeks){
23029                 v.remove();
23030                 return;
23031             }
23032             
23033             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23034             v.attr('colspan', function(i, val){
23035                 return parseInt(val) + 1;
23036             });
23037         });
23038                         
23039         
23040         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23041         
23042         this.setStartDate(this.startDate);
23043         this.setEndDate(this.endDate);
23044         
23045         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23046         
23047         this.fillDow();
23048         this.fillMonths();
23049         this.update();
23050         this.showMode();
23051         
23052         if(this.isInline) {
23053             this.showPopup();
23054         }
23055     },
23056     
23057     picker : function()
23058     {
23059         return this.pickerEl;
23060 //        return this.el.select('.datepicker', true).first();
23061     },
23062     
23063     fillDow: function()
23064     {
23065         var dowCnt = this.weekStart;
23066         
23067         var dow = {
23068             tag: 'tr',
23069             cn: [
23070                 
23071             ]
23072         };
23073         
23074         if(this.calendarWeeks){
23075             dow.cn.push({
23076                 tag: 'th',
23077                 cls: 'cw',
23078                 html: '&nbsp;'
23079             })
23080         }
23081         
23082         while (dowCnt < this.weekStart + 7) {
23083             dow.cn.push({
23084                 tag: 'th',
23085                 cls: 'dow',
23086                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23087             });
23088         }
23089         
23090         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23091     },
23092     
23093     fillMonths: function()
23094     {    
23095         var i = 0;
23096         var months = this.picker().select('>.datepicker-months td', true).first();
23097         
23098         months.dom.innerHTML = '';
23099         
23100         while (i < 12) {
23101             var month = {
23102                 tag: 'span',
23103                 cls: 'month',
23104                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23105             };
23106             
23107             months.createChild(month);
23108         }
23109         
23110     },
23111     
23112     update: function()
23113     {
23114         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;
23115         
23116         if (this.date < this.startDate) {
23117             this.viewDate = new Date(this.startDate);
23118         } else if (this.date > this.endDate) {
23119             this.viewDate = new Date(this.endDate);
23120         } else {
23121             this.viewDate = new Date(this.date);
23122         }
23123         
23124         this.fill();
23125     },
23126     
23127     fill: function() 
23128     {
23129         var d = new Date(this.viewDate),
23130                 year = d.getUTCFullYear(),
23131                 month = d.getUTCMonth(),
23132                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23133                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23134                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23135                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23136                 currentDate = this.date && this.date.valueOf(),
23137                 today = this.UTCToday();
23138         
23139         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23140         
23141 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23142         
23143 //        this.picker.select('>tfoot th.today').
23144 //                                              .text(dates[this.language].today)
23145 //                                              .toggle(this.todayBtn !== false);
23146     
23147         this.updateNavArrows();
23148         this.fillMonths();
23149                                                 
23150         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23151         
23152         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23153          
23154         prevMonth.setUTCDate(day);
23155         
23156         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23157         
23158         var nextMonth = new Date(prevMonth);
23159         
23160         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23161         
23162         nextMonth = nextMonth.valueOf();
23163         
23164         var fillMonths = false;
23165         
23166         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23167         
23168         while(prevMonth.valueOf() <= nextMonth) {
23169             var clsName = '';
23170             
23171             if (prevMonth.getUTCDay() === this.weekStart) {
23172                 if(fillMonths){
23173                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23174                 }
23175                     
23176                 fillMonths = {
23177                     tag: 'tr',
23178                     cn: []
23179                 };
23180                 
23181                 if(this.calendarWeeks){
23182                     // ISO 8601: First week contains first thursday.
23183                     // ISO also states week starts on Monday, but we can be more abstract here.
23184                     var
23185                     // Start of current week: based on weekstart/current date
23186                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23187                     // Thursday of this week
23188                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23189                     // First Thursday of year, year from thursday
23190                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23191                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23192                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23193                     
23194                     fillMonths.cn.push({
23195                         tag: 'td',
23196                         cls: 'cw',
23197                         html: calWeek
23198                     });
23199                 }
23200             }
23201             
23202             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23203                 clsName += ' old';
23204             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23205                 clsName += ' new';
23206             }
23207             if (this.todayHighlight &&
23208                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23209                 prevMonth.getUTCMonth() == today.getMonth() &&
23210                 prevMonth.getUTCDate() == today.getDate()) {
23211                 clsName += ' today';
23212             }
23213             
23214             if (currentDate && prevMonth.valueOf() === currentDate) {
23215                 clsName += ' active';
23216             }
23217             
23218             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23219                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23220                     clsName += ' disabled';
23221             }
23222             
23223             fillMonths.cn.push({
23224                 tag: 'td',
23225                 cls: 'day ' + clsName,
23226                 html: prevMonth.getDate()
23227             });
23228             
23229             prevMonth.setDate(prevMonth.getDate()+1);
23230         }
23231           
23232         var currentYear = this.date && this.date.getUTCFullYear();
23233         var currentMonth = this.date && this.date.getUTCMonth();
23234         
23235         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23236         
23237         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23238             v.removeClass('active');
23239             
23240             if(currentYear === year && k === currentMonth){
23241                 v.addClass('active');
23242             }
23243             
23244             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23245                 v.addClass('disabled');
23246             }
23247             
23248         });
23249         
23250         
23251         year = parseInt(year/10, 10) * 10;
23252         
23253         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23254         
23255         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23256         
23257         year -= 1;
23258         for (var i = -1; i < 11; i++) {
23259             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23260                 tag: 'span',
23261                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23262                 html: year
23263             });
23264             
23265             year += 1;
23266         }
23267     },
23268     
23269     showMode: function(dir) 
23270     {
23271         if (dir) {
23272             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23273         }
23274         
23275         Roo.each(this.picker().select('>div',true).elements, function(v){
23276             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23277             v.hide();
23278         });
23279         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23280     },
23281     
23282     place: function()
23283     {
23284         if(this.isInline) {
23285             return;
23286         }
23287         
23288         this.picker().removeClass(['bottom', 'top']);
23289         
23290         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23291             /*
23292              * place to the top of element!
23293              *
23294              */
23295             
23296             this.picker().addClass('top');
23297             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23298             
23299             return;
23300         }
23301         
23302         this.picker().addClass('bottom');
23303         
23304         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23305     },
23306     
23307     parseDate : function(value)
23308     {
23309         if(!value || value instanceof Date){
23310             return value;
23311         }
23312         var v = Date.parseDate(value, this.format);
23313         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23314             v = Date.parseDate(value, 'Y-m-d');
23315         }
23316         if(!v && this.altFormats){
23317             if(!this.altFormatsArray){
23318                 this.altFormatsArray = this.altFormats.split("|");
23319             }
23320             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23321                 v = Date.parseDate(value, this.altFormatsArray[i]);
23322             }
23323         }
23324         return v;
23325     },
23326     
23327     formatDate : function(date, fmt)
23328     {   
23329         return (!date || !(date instanceof Date)) ?
23330         date : date.dateFormat(fmt || this.format);
23331     },
23332     
23333     onFocus : function()
23334     {
23335         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23336         this.showPopup();
23337     },
23338     
23339     onBlur : function()
23340     {
23341         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23342         
23343         var d = this.inputEl().getValue();
23344         
23345         this.setValue(d);
23346                 
23347         this.hidePopup();
23348     },
23349     
23350     showPopup : function()
23351     {
23352         this.picker().show();
23353         this.update();
23354         this.place();
23355         
23356         this.fireEvent('showpopup', this, this.date);
23357     },
23358     
23359     hidePopup : function()
23360     {
23361         if(this.isInline) {
23362             return;
23363         }
23364         this.picker().hide();
23365         this.viewMode = this.startViewMode;
23366         this.showMode();
23367         
23368         this.fireEvent('hidepopup', this, this.date);
23369         
23370     },
23371     
23372     onMousedown: function(e)
23373     {
23374         e.stopPropagation();
23375         e.preventDefault();
23376     },
23377     
23378     keyup: function(e)
23379     {
23380         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23381         this.update();
23382     },
23383
23384     setValue: function(v)
23385     {
23386         if(this.fireEvent('beforeselect', this, v) !== false){
23387             var d = new Date(this.parseDate(v) ).clearTime();
23388         
23389             if(isNaN(d.getTime())){
23390                 this.date = this.viewDate = '';
23391                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23392                 return;
23393             }
23394
23395             v = this.formatDate(d);
23396
23397             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23398
23399             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23400
23401             this.update();
23402
23403             this.fireEvent('select', this, this.date);
23404         }
23405     },
23406     
23407     getValue: function()
23408     {
23409         return this.formatDate(this.date);
23410     },
23411     
23412     fireKey: function(e)
23413     {
23414         if (!this.picker().isVisible()){
23415             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23416                 this.showPopup();
23417             }
23418             return;
23419         }
23420         
23421         var dateChanged = false,
23422         dir, day, month,
23423         newDate, newViewDate;
23424         
23425         switch(e.keyCode){
23426             case 27: // escape
23427                 this.hidePopup();
23428                 e.preventDefault();
23429                 break;
23430             case 37: // left
23431             case 39: // right
23432                 if (!this.keyboardNavigation) {
23433                     break;
23434                 }
23435                 dir = e.keyCode == 37 ? -1 : 1;
23436                 
23437                 if (e.ctrlKey){
23438                     newDate = this.moveYear(this.date, dir);
23439                     newViewDate = this.moveYear(this.viewDate, dir);
23440                 } else if (e.shiftKey){
23441                     newDate = this.moveMonth(this.date, dir);
23442                     newViewDate = this.moveMonth(this.viewDate, dir);
23443                 } else {
23444                     newDate = new Date(this.date);
23445                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23446                     newViewDate = new Date(this.viewDate);
23447                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23448                 }
23449                 if (this.dateWithinRange(newDate)){
23450                     this.date = newDate;
23451                     this.viewDate = newViewDate;
23452                     this.setValue(this.formatDate(this.date));
23453 //                    this.update();
23454                     e.preventDefault();
23455                     dateChanged = true;
23456                 }
23457                 break;
23458             case 38: // up
23459             case 40: // down
23460                 if (!this.keyboardNavigation) {
23461                     break;
23462                 }
23463                 dir = e.keyCode == 38 ? -1 : 1;
23464                 if (e.ctrlKey){
23465                     newDate = this.moveYear(this.date, dir);
23466                     newViewDate = this.moveYear(this.viewDate, dir);
23467                 } else if (e.shiftKey){
23468                     newDate = this.moveMonth(this.date, dir);
23469                     newViewDate = this.moveMonth(this.viewDate, dir);
23470                 } else {
23471                     newDate = new Date(this.date);
23472                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23473                     newViewDate = new Date(this.viewDate);
23474                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23475                 }
23476                 if (this.dateWithinRange(newDate)){
23477                     this.date = newDate;
23478                     this.viewDate = newViewDate;
23479                     this.setValue(this.formatDate(this.date));
23480 //                    this.update();
23481                     e.preventDefault();
23482                     dateChanged = true;
23483                 }
23484                 break;
23485             case 13: // enter
23486                 this.setValue(this.formatDate(this.date));
23487                 this.hidePopup();
23488                 e.preventDefault();
23489                 break;
23490             case 9: // tab
23491                 this.setValue(this.formatDate(this.date));
23492                 this.hidePopup();
23493                 break;
23494             case 16: // shift
23495             case 17: // ctrl
23496             case 18: // alt
23497                 break;
23498             default :
23499                 this.hidePopup();
23500                 
23501         }
23502     },
23503     
23504     
23505     onClick: function(e) 
23506     {
23507         e.stopPropagation();
23508         e.preventDefault();
23509         
23510         var target = e.getTarget();
23511         
23512         if(target.nodeName.toLowerCase() === 'i'){
23513             target = Roo.get(target).dom.parentNode;
23514         }
23515         
23516         var nodeName = target.nodeName;
23517         var className = target.className;
23518         var html = target.innerHTML;
23519         //Roo.log(nodeName);
23520         
23521         switch(nodeName.toLowerCase()) {
23522             case 'th':
23523                 switch(className) {
23524                     case 'switch':
23525                         this.showMode(1);
23526                         break;
23527                     case 'prev':
23528                     case 'next':
23529                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23530                         switch(this.viewMode){
23531                                 case 0:
23532                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23533                                         break;
23534                                 case 1:
23535                                 case 2:
23536                                         this.viewDate = this.moveYear(this.viewDate, dir);
23537                                         break;
23538                         }
23539                         this.fill();
23540                         break;
23541                     case 'today':
23542                         var date = new Date();
23543                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23544 //                        this.fill()
23545                         this.setValue(this.formatDate(this.date));
23546                         
23547                         this.hidePopup();
23548                         break;
23549                 }
23550                 break;
23551             case 'span':
23552                 if (className.indexOf('disabled') < 0) {
23553                 if (!this.viewDate) {
23554                     this.viewDate = new Date();
23555                 }
23556                 this.viewDate.setUTCDate(1);
23557                     if (className.indexOf('month') > -1) {
23558                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23559                     } else {
23560                         var year = parseInt(html, 10) || 0;
23561                         this.viewDate.setUTCFullYear(year);
23562                         
23563                     }
23564                     
23565                     if(this.singleMode){
23566                         this.setValue(this.formatDate(this.viewDate));
23567                         this.hidePopup();
23568                         return;
23569                     }
23570                     
23571                     this.showMode(-1);
23572                     this.fill();
23573                 }
23574                 break;
23575                 
23576             case 'td':
23577                 //Roo.log(className);
23578                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23579                     var day = parseInt(html, 10) || 1;
23580                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23581                         month = (this.viewDate || new Date()).getUTCMonth();
23582
23583                     if (className.indexOf('old') > -1) {
23584                         if(month === 0 ){
23585                             month = 11;
23586                             year -= 1;
23587                         }else{
23588                             month -= 1;
23589                         }
23590                     } else if (className.indexOf('new') > -1) {
23591                         if (month == 11) {
23592                             month = 0;
23593                             year += 1;
23594                         } else {
23595                             month += 1;
23596                         }
23597                     }
23598                     //Roo.log([year,month,day]);
23599                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23600                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23601 //                    this.fill();
23602                     //Roo.log(this.formatDate(this.date));
23603                     this.setValue(this.formatDate(this.date));
23604                     this.hidePopup();
23605                 }
23606                 break;
23607         }
23608     },
23609     
23610     setStartDate: function(startDate)
23611     {
23612         this.startDate = startDate || -Infinity;
23613         if (this.startDate !== -Infinity) {
23614             this.startDate = this.parseDate(this.startDate);
23615         }
23616         this.update();
23617         this.updateNavArrows();
23618     },
23619
23620     setEndDate: function(endDate)
23621     {
23622         this.endDate = endDate || Infinity;
23623         if (this.endDate !== Infinity) {
23624             this.endDate = this.parseDate(this.endDate);
23625         }
23626         this.update();
23627         this.updateNavArrows();
23628     },
23629     
23630     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23631     {
23632         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23633         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23634             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23635         }
23636         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23637             return parseInt(d, 10);
23638         });
23639         this.update();
23640         this.updateNavArrows();
23641     },
23642     
23643     updateNavArrows: function() 
23644     {
23645         if(this.singleMode){
23646             return;
23647         }
23648         
23649         var d = new Date(this.viewDate),
23650         year = d.getUTCFullYear(),
23651         month = d.getUTCMonth();
23652         
23653         Roo.each(this.picker().select('.prev', true).elements, function(v){
23654             v.show();
23655             switch (this.viewMode) {
23656                 case 0:
23657
23658                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23659                         v.hide();
23660                     }
23661                     break;
23662                 case 1:
23663                 case 2:
23664                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23665                         v.hide();
23666                     }
23667                     break;
23668             }
23669         });
23670         
23671         Roo.each(this.picker().select('.next', true).elements, function(v){
23672             v.show();
23673             switch (this.viewMode) {
23674                 case 0:
23675
23676                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23677                         v.hide();
23678                     }
23679                     break;
23680                 case 1:
23681                 case 2:
23682                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23683                         v.hide();
23684                     }
23685                     break;
23686             }
23687         })
23688     },
23689     
23690     moveMonth: function(date, dir)
23691     {
23692         if (!dir) {
23693             return date;
23694         }
23695         var new_date = new Date(date.valueOf()),
23696         day = new_date.getUTCDate(),
23697         month = new_date.getUTCMonth(),
23698         mag = Math.abs(dir),
23699         new_month, test;
23700         dir = dir > 0 ? 1 : -1;
23701         if (mag == 1){
23702             test = dir == -1
23703             // If going back one month, make sure month is not current month
23704             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23705             ? function(){
23706                 return new_date.getUTCMonth() == month;
23707             }
23708             // If going forward one month, make sure month is as expected
23709             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23710             : function(){
23711                 return new_date.getUTCMonth() != new_month;
23712             };
23713             new_month = month + dir;
23714             new_date.setUTCMonth(new_month);
23715             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23716             if (new_month < 0 || new_month > 11) {
23717                 new_month = (new_month + 12) % 12;
23718             }
23719         } else {
23720             // For magnitudes >1, move one month at a time...
23721             for (var i=0; i<mag; i++) {
23722                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23723                 new_date = this.moveMonth(new_date, dir);
23724             }
23725             // ...then reset the day, keeping it in the new month
23726             new_month = new_date.getUTCMonth();
23727             new_date.setUTCDate(day);
23728             test = function(){
23729                 return new_month != new_date.getUTCMonth();
23730             };
23731         }
23732         // Common date-resetting loop -- if date is beyond end of month, make it
23733         // end of month
23734         while (test()){
23735             new_date.setUTCDate(--day);
23736             new_date.setUTCMonth(new_month);
23737         }
23738         return new_date;
23739     },
23740
23741     moveYear: function(date, dir)
23742     {
23743         return this.moveMonth(date, dir*12);
23744     },
23745
23746     dateWithinRange: function(date)
23747     {
23748         return date >= this.startDate && date <= this.endDate;
23749     },
23750
23751     
23752     remove: function() 
23753     {
23754         this.picker().remove();
23755     },
23756     
23757     validateValue : function(value)
23758     {
23759         if(this.getVisibilityEl().hasClass('hidden')){
23760             return true;
23761         }
23762         
23763         if(value.length < 1)  {
23764             if(this.allowBlank){
23765                 return true;
23766             }
23767             return false;
23768         }
23769         
23770         if(value.length < this.minLength){
23771             return false;
23772         }
23773         if(value.length > this.maxLength){
23774             return false;
23775         }
23776         if(this.vtype){
23777             var vt = Roo.form.VTypes;
23778             if(!vt[this.vtype](value, this)){
23779                 return false;
23780             }
23781         }
23782         if(typeof this.validator == "function"){
23783             var msg = this.validator(value);
23784             if(msg !== true){
23785                 return false;
23786             }
23787         }
23788         
23789         if(this.regex && !this.regex.test(value)){
23790             return false;
23791         }
23792         
23793         if(typeof(this.parseDate(value)) == 'undefined'){
23794             return false;
23795         }
23796         
23797         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23798             return false;
23799         }      
23800         
23801         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23802             return false;
23803         } 
23804         
23805         
23806         return true;
23807     },
23808     
23809     reset : function()
23810     {
23811         this.date = this.viewDate = '';
23812         
23813         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23814     }
23815    
23816 });
23817
23818 Roo.apply(Roo.bootstrap.form.DateField,  {
23819     
23820     head : {
23821         tag: 'thead',
23822         cn: [
23823         {
23824             tag: 'tr',
23825             cn: [
23826             {
23827                 tag: 'th',
23828                 cls: 'prev',
23829                 html: '<i class="fa fa-arrow-left"/>'
23830             },
23831             {
23832                 tag: 'th',
23833                 cls: 'switch',
23834                 colspan: '5'
23835             },
23836             {
23837                 tag: 'th',
23838                 cls: 'next',
23839                 html: '<i class="fa fa-arrow-right"/>'
23840             }
23841
23842             ]
23843         }
23844         ]
23845     },
23846     
23847     content : {
23848         tag: 'tbody',
23849         cn: [
23850         {
23851             tag: 'tr',
23852             cn: [
23853             {
23854                 tag: 'td',
23855                 colspan: '7'
23856             }
23857             ]
23858         }
23859         ]
23860     },
23861     
23862     footer : {
23863         tag: 'tfoot',
23864         cn: [
23865         {
23866             tag: 'tr',
23867             cn: [
23868             {
23869                 tag: 'th',
23870                 colspan: '7',
23871                 cls: 'today'
23872             }
23873                     
23874             ]
23875         }
23876         ]
23877     },
23878     
23879     dates:{
23880         en: {
23881             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23882             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23883             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23884             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23885             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23886             today: "Today"
23887         }
23888     },
23889     
23890     modes: [
23891     {
23892         clsName: 'days',
23893         navFnc: 'Month',
23894         navStep: 1
23895     },
23896     {
23897         clsName: 'months',
23898         navFnc: 'FullYear',
23899         navStep: 1
23900     },
23901     {
23902         clsName: 'years',
23903         navFnc: 'FullYear',
23904         navStep: 10
23905     }]
23906 });
23907
23908 Roo.apply(Roo.bootstrap.form.DateField,  {
23909   
23910     template : {
23911         tag: 'div',
23912         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23913         cn: [
23914         {
23915             tag: 'div',
23916             cls: 'datepicker-days',
23917             cn: [
23918             {
23919                 tag: 'table',
23920                 cls: 'table-condensed',
23921                 cn:[
23922                 Roo.bootstrap.form.DateField.head,
23923                 {
23924                     tag: 'tbody'
23925                 },
23926                 Roo.bootstrap.form.DateField.footer
23927                 ]
23928             }
23929             ]
23930         },
23931         {
23932             tag: 'div',
23933             cls: 'datepicker-months',
23934             cn: [
23935             {
23936                 tag: 'table',
23937                 cls: 'table-condensed',
23938                 cn:[
23939                 Roo.bootstrap.form.DateField.head,
23940                 Roo.bootstrap.form.DateField.content,
23941                 Roo.bootstrap.form.DateField.footer
23942                 ]
23943             }
23944             ]
23945         },
23946         {
23947             tag: 'div',
23948             cls: 'datepicker-years',
23949             cn: [
23950             {
23951                 tag: 'table',
23952                 cls: 'table-condensed',
23953                 cn:[
23954                 Roo.bootstrap.form.DateField.head,
23955                 Roo.bootstrap.form.DateField.content,
23956                 Roo.bootstrap.form.DateField.footer
23957                 ]
23958             }
23959             ]
23960         }
23961         ]
23962     }
23963 });
23964
23965  
23966
23967  /*
23968  * - LGPL
23969  *
23970  * TimeField
23971  * 
23972  */
23973
23974 /**
23975  * @class Roo.bootstrap.form.TimeField
23976  * @extends Roo.bootstrap.form.Input
23977  * Bootstrap DateField class
23978  * 
23979  * 
23980  * @constructor
23981  * Create a new TimeField
23982  * @param {Object} config The config object
23983  */
23984
23985 Roo.bootstrap.form.TimeField = function(config){
23986     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23987     this.addEvents({
23988             /**
23989              * @event show
23990              * Fires when this field show.
23991              * @param {Roo.bootstrap.form.DateField} thisthis
23992              * @param {Mixed} date The date value
23993              */
23994             show : true,
23995             /**
23996              * @event show
23997              * Fires when this field hide.
23998              * @param {Roo.bootstrap.form.DateField} this
23999              * @param {Mixed} date The date value
24000              */
24001             hide : true,
24002             /**
24003              * @event select
24004              * Fires when select a date.
24005              * @param {Roo.bootstrap.form.DateField} this
24006              * @param {Mixed} date The date value
24007              */
24008             select : true
24009         });
24010 };
24011
24012 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
24013     
24014     /**
24015      * @cfg {String} format
24016      * The default time format string which can be overriden for localization support.  The format must be
24017      * valid according to {@link Date#parseDate} (defaults to 'H:i').
24018      */
24019     format : "H:i",
24020
24021     getAutoCreate : function()
24022     {
24023         this.after = '<i class="fa far fa-clock"></i>';
24024         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24025         
24026          
24027     },
24028     onRender: function(ct, position)
24029     {
24030         
24031         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24032                 
24033         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24034         
24035         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24036         
24037         this.pop = this.picker().select('>.datepicker-time',true).first();
24038         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24039         
24040         this.picker().on('mousedown', this.onMousedown, this);
24041         this.picker().on('click', this.onClick, this);
24042         
24043         this.picker().addClass('datepicker-dropdown');
24044     
24045         this.fillTime();
24046         this.update();
24047             
24048         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24049         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24050         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24051         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24052         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24053         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24054
24055     },
24056     
24057     fireKey: function(e){
24058         if (!this.picker().isVisible()){
24059             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24060                 this.show();
24061             }
24062             return;
24063         }
24064
24065         e.preventDefault();
24066         
24067         switch(e.keyCode){
24068             case 27: // escape
24069                 this.hide();
24070                 break;
24071             case 37: // left
24072             case 39: // right
24073                 this.onTogglePeriod();
24074                 break;
24075             case 38: // up
24076                 this.onIncrementMinutes();
24077                 break;
24078             case 40: // down
24079                 this.onDecrementMinutes();
24080                 break;
24081             case 13: // enter
24082             case 9: // tab
24083                 this.setTime();
24084                 break;
24085         }
24086     },
24087     
24088     onClick: function(e) {
24089         e.stopPropagation();
24090         e.preventDefault();
24091     },
24092     
24093     picker : function()
24094     {
24095         return this.pickerEl;
24096     },
24097     
24098     fillTime: function()
24099     {    
24100         var time = this.pop.select('tbody', true).first();
24101         
24102         time.dom.innerHTML = '';
24103         
24104         time.createChild({
24105             tag: 'tr',
24106             cn: [
24107                 {
24108                     tag: 'td',
24109                     cn: [
24110                         {
24111                             tag: 'a',
24112                             href: '#',
24113                             cls: 'btn',
24114                             cn: [
24115                                 {
24116                                     tag: 'i',
24117                                     cls: 'hours-up fa fas fa-chevron-up'
24118                                 }
24119                             ]
24120                         } 
24121                     ]
24122                 },
24123                 {
24124                     tag: 'td',
24125                     cls: 'separator'
24126                 },
24127                 {
24128                     tag: 'td',
24129                     cn: [
24130                         {
24131                             tag: 'a',
24132                             href: '#',
24133                             cls: 'btn',
24134                             cn: [
24135                                 {
24136                                     tag: 'i',
24137                                     cls: 'minutes-up fa fas fa-chevron-up'
24138                                 }
24139                             ]
24140                         }
24141                     ]
24142                 },
24143                 {
24144                     tag: 'td',
24145                     cls: 'separator'
24146                 }
24147             ]
24148         });
24149         
24150         time.createChild({
24151             tag: 'tr',
24152             cn: [
24153                 {
24154                     tag: 'td',
24155                     cn: [
24156                         {
24157                             tag: 'span',
24158                             cls: 'timepicker-hour',
24159                             html: '00'
24160                         }  
24161                     ]
24162                 },
24163                 {
24164                     tag: 'td',
24165                     cls: 'separator',
24166                     html: ':'
24167                 },
24168                 {
24169                     tag: 'td',
24170                     cn: [
24171                         {
24172                             tag: 'span',
24173                             cls: 'timepicker-minute',
24174                             html: '00'
24175                         }  
24176                     ]
24177                 },
24178                 {
24179                     tag: 'td',
24180                     cls: 'separator'
24181                 },
24182                 {
24183                     tag: 'td',
24184                     cn: [
24185                         {
24186                             tag: 'button',
24187                             type: 'button',
24188                             cls: 'btn btn-primary period',
24189                             html: 'AM'
24190                             
24191                         }
24192                     ]
24193                 }
24194             ]
24195         });
24196         
24197         time.createChild({
24198             tag: 'tr',
24199             cn: [
24200                 {
24201                     tag: 'td',
24202                     cn: [
24203                         {
24204                             tag: 'a',
24205                             href: '#',
24206                             cls: 'btn',
24207                             cn: [
24208                                 {
24209                                     tag: 'span',
24210                                     cls: 'hours-down fa fas fa-chevron-down'
24211                                 }
24212                             ]
24213                         }
24214                     ]
24215                 },
24216                 {
24217                     tag: 'td',
24218                     cls: 'separator'
24219                 },
24220                 {
24221                     tag: 'td',
24222                     cn: [
24223                         {
24224                             tag: 'a',
24225                             href: '#',
24226                             cls: 'btn',
24227                             cn: [
24228                                 {
24229                                     tag: 'span',
24230                                     cls: 'minutes-down fa fas fa-chevron-down'
24231                                 }
24232                             ]
24233                         }
24234                     ]
24235                 },
24236                 {
24237                     tag: 'td',
24238                     cls: 'separator'
24239                 }
24240             ]
24241         });
24242         
24243     },
24244     
24245     update: function()
24246     {
24247         
24248         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24249         
24250         this.fill();
24251     },
24252     
24253     fill: function() 
24254     {
24255         var hours = this.time.getHours();
24256         var minutes = this.time.getMinutes();
24257         var period = 'AM';
24258         
24259         if(hours > 11){
24260             period = 'PM';
24261         }
24262         
24263         if(hours == 0){
24264             hours = 12;
24265         }
24266         
24267         
24268         if(hours > 12){
24269             hours = hours - 12;
24270         }
24271         
24272         if(hours < 10){
24273             hours = '0' + hours;
24274         }
24275         
24276         if(minutes < 10){
24277             minutes = '0' + minutes;
24278         }
24279         
24280         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24281         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24282         this.pop.select('button', true).first().dom.innerHTML = period;
24283         
24284     },
24285     
24286     place: function()
24287     {   
24288         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24289         
24290         var cls = ['bottom'];
24291         
24292         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24293             cls.pop();
24294             cls.push('top');
24295         }
24296         
24297         cls.push('right');
24298         
24299         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24300             cls.pop();
24301             cls.push('left');
24302         }
24303         //this.picker().setXY(20000,20000);
24304         this.picker().addClass(cls.join('-'));
24305         
24306         var _this = this;
24307         
24308         Roo.each(cls, function(c){
24309             if(c == 'bottom'){
24310                 (function() {
24311                  //  
24312                 }).defer(200);
24313                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24314                 //_this.picker().setTop(_this.inputEl().getHeight());
24315                 return;
24316             }
24317             if(c == 'top'){
24318                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24319                 
24320                 //_this.picker().setTop(0 - _this.picker().getHeight());
24321                 return;
24322             }
24323             /*
24324             if(c == 'left'){
24325                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24326                 return;
24327             }
24328             if(c == 'right'){
24329                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24330                 return;
24331             }
24332             */
24333         });
24334         
24335     },
24336   
24337     onFocus : function()
24338     {
24339         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24340         this.show();
24341     },
24342     
24343     onBlur : function()
24344     {
24345         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24346         this.hide();
24347     },
24348     
24349     show : function()
24350     {
24351         this.picker().show();
24352         this.pop.show();
24353         this.update();
24354         this.place();
24355         
24356         this.fireEvent('show', this, this.date);
24357     },
24358     
24359     hide : function()
24360     {
24361         this.picker().hide();
24362         this.pop.hide();
24363         
24364         this.fireEvent('hide', this, this.date);
24365     },
24366     
24367     setTime : function()
24368     {
24369         this.hide();
24370         this.setValue(this.time.format(this.format));
24371         
24372         this.fireEvent('select', this, this.date);
24373         
24374         
24375     },
24376     
24377     onMousedown: function(e){
24378         e.stopPropagation();
24379         e.preventDefault();
24380     },
24381     
24382     onIncrementHours: function()
24383     {
24384         Roo.log('onIncrementHours');
24385         this.time = this.time.add(Date.HOUR, 1);
24386         this.update();
24387         
24388     },
24389     
24390     onDecrementHours: function()
24391     {
24392         Roo.log('onDecrementHours');
24393         this.time = this.time.add(Date.HOUR, -1);
24394         this.update();
24395     },
24396     
24397     onIncrementMinutes: function()
24398     {
24399         Roo.log('onIncrementMinutes');
24400         this.time = this.time.add(Date.MINUTE, 1);
24401         this.update();
24402     },
24403     
24404     onDecrementMinutes: function()
24405     {
24406         Roo.log('onDecrementMinutes');
24407         this.time = this.time.add(Date.MINUTE, -1);
24408         this.update();
24409     },
24410     
24411     onTogglePeriod: function()
24412     {
24413         Roo.log('onTogglePeriod');
24414         this.time = this.time.add(Date.HOUR, 12);
24415         this.update();
24416     }
24417     
24418    
24419 });
24420  
24421
24422 Roo.apply(Roo.bootstrap.form.TimeField,  {
24423   
24424     template : {
24425         tag: 'div',
24426         cls: 'datepicker dropdown-menu',
24427         cn: [
24428             {
24429                 tag: 'div',
24430                 cls: 'datepicker-time',
24431                 cn: [
24432                 {
24433                     tag: 'table',
24434                     cls: 'table-condensed',
24435                     cn:[
24436                         {
24437                             tag: 'tbody',
24438                             cn: [
24439                                 {
24440                                     tag: 'tr',
24441                                     cn: [
24442                                     {
24443                                         tag: 'td',
24444                                         colspan: '7'
24445                                     }
24446                                     ]
24447                                 }
24448                             ]
24449                         },
24450                         {
24451                             tag: 'tfoot',
24452                             cn: [
24453                                 {
24454                                     tag: 'tr',
24455                                     cn: [
24456                                     {
24457                                         tag: 'th',
24458                                         colspan: '7',
24459                                         cls: '',
24460                                         cn: [
24461                                             {
24462                                                 tag: 'button',
24463                                                 cls: 'btn btn-info ok',
24464                                                 html: 'OK'
24465                                             }
24466                                         ]
24467                                     }
24468                     
24469                                     ]
24470                                 }
24471                             ]
24472                         }
24473                     ]
24474                 }
24475                 ]
24476             }
24477         ]
24478     }
24479 });
24480
24481  
24482
24483  /*
24484  * - LGPL
24485  *
24486  * MonthField
24487  * 
24488  */
24489
24490 /**
24491  * @class Roo.bootstrap.form.MonthField
24492  * @extends Roo.bootstrap.form.Input
24493  * Bootstrap MonthField class
24494  * 
24495  * @cfg {String} language default en
24496  * 
24497  * @constructor
24498  * Create a new MonthField
24499  * @param {Object} config The config object
24500  */
24501
24502 Roo.bootstrap.form.MonthField = function(config){
24503     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24504     
24505     this.addEvents({
24506         /**
24507          * @event show
24508          * Fires when this field show.
24509          * @param {Roo.bootstrap.form.MonthField} this
24510          * @param {Mixed} date The date value
24511          */
24512         show : true,
24513         /**
24514          * @event show
24515          * Fires when this field hide.
24516          * @param {Roo.bootstrap.form.MonthField} this
24517          * @param {Mixed} date The date value
24518          */
24519         hide : true,
24520         /**
24521          * @event select
24522          * Fires when select a date.
24523          * @param {Roo.bootstrap.form.MonthField} this
24524          * @param {String} oldvalue The old value
24525          * @param {String} newvalue The new value
24526          */
24527         select : true
24528     });
24529 };
24530
24531 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24532     
24533     onRender: function(ct, position)
24534     {
24535         
24536         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24537         
24538         this.language = this.language || 'en';
24539         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24540         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24541         
24542         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24543         this.isInline = false;
24544         this.isInput = true;
24545         this.component = this.el.select('.add-on', true).first() || false;
24546         this.component = (this.component && this.component.length === 0) ? false : this.component;
24547         this.hasInput = this.component && this.inputEL().length;
24548         
24549         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24550         
24551         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24552         
24553         this.picker().on('mousedown', this.onMousedown, this);
24554         this.picker().on('click', this.onClick, this);
24555         
24556         this.picker().addClass('datepicker-dropdown');
24557         
24558         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24559             v.setStyle('width', '189px');
24560         });
24561         
24562         this.fillMonths();
24563         
24564         this.update();
24565         
24566         if(this.isInline) {
24567             this.show();
24568         }
24569         
24570     },
24571     
24572     setValue: function(v, suppressEvent)
24573     {   
24574         var o = this.getValue();
24575         
24576         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24577         
24578         this.update();
24579
24580         if(suppressEvent !== true){
24581             this.fireEvent('select', this, o, v);
24582         }
24583         
24584     },
24585     
24586     getValue: function()
24587     {
24588         return this.value;
24589     },
24590     
24591     onClick: function(e) 
24592     {
24593         e.stopPropagation();
24594         e.preventDefault();
24595         
24596         var target = e.getTarget();
24597         
24598         if(target.nodeName.toLowerCase() === 'i'){
24599             target = Roo.get(target).dom.parentNode;
24600         }
24601         
24602         var nodeName = target.nodeName;
24603         var className = target.className;
24604         var html = target.innerHTML;
24605         
24606         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24607             return;
24608         }
24609         
24610         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24611         
24612         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24613         
24614         this.hide();
24615                         
24616     },
24617     
24618     picker : function()
24619     {
24620         return this.pickerEl;
24621     },
24622     
24623     fillMonths: function()
24624     {    
24625         var i = 0;
24626         var months = this.picker().select('>.datepicker-months td', true).first();
24627         
24628         months.dom.innerHTML = '';
24629         
24630         while (i < 12) {
24631             var month = {
24632                 tag: 'span',
24633                 cls: 'month',
24634                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24635             };
24636             
24637             months.createChild(month);
24638         }
24639         
24640     },
24641     
24642     update: function()
24643     {
24644         var _this = this;
24645         
24646         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24647             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24648         }
24649         
24650         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24651             e.removeClass('active');
24652             
24653             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24654                 e.addClass('active');
24655             }
24656         })
24657     },
24658     
24659     place: function()
24660     {
24661         if(this.isInline) {
24662             return;
24663         }
24664         
24665         this.picker().removeClass(['bottom', 'top']);
24666         
24667         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24668             /*
24669              * place to the top of element!
24670              *
24671              */
24672             
24673             this.picker().addClass('top');
24674             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24675             
24676             return;
24677         }
24678         
24679         this.picker().addClass('bottom');
24680         
24681         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24682     },
24683     
24684     onFocus : function()
24685     {
24686         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24687         this.show();
24688     },
24689     
24690     onBlur : function()
24691     {
24692         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24693         
24694         var d = this.inputEl().getValue();
24695         
24696         this.setValue(d);
24697                 
24698         this.hide();
24699     },
24700     
24701     show : function()
24702     {
24703         this.picker().show();
24704         this.picker().select('>.datepicker-months', true).first().show();
24705         this.update();
24706         this.place();
24707         
24708         this.fireEvent('show', this, this.date);
24709     },
24710     
24711     hide : function()
24712     {
24713         if(this.isInline) {
24714             return;
24715         }
24716         this.picker().hide();
24717         this.fireEvent('hide', this, this.date);
24718         
24719     },
24720     
24721     onMousedown: function(e)
24722     {
24723         e.stopPropagation();
24724         e.preventDefault();
24725     },
24726     
24727     keyup: function(e)
24728     {
24729         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24730         this.update();
24731     },
24732
24733     fireKey: function(e)
24734     {
24735         if (!this.picker().isVisible()){
24736             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24737                 this.show();
24738             }
24739             return;
24740         }
24741         
24742         var dir;
24743         
24744         switch(e.keyCode){
24745             case 27: // escape
24746                 this.hide();
24747                 e.preventDefault();
24748                 break;
24749             case 37: // left
24750             case 39: // right
24751                 dir = e.keyCode == 37 ? -1 : 1;
24752                 
24753                 this.vIndex = this.vIndex + dir;
24754                 
24755                 if(this.vIndex < 0){
24756                     this.vIndex = 0;
24757                 }
24758                 
24759                 if(this.vIndex > 11){
24760                     this.vIndex = 11;
24761                 }
24762                 
24763                 if(isNaN(this.vIndex)){
24764                     this.vIndex = 0;
24765                 }
24766                 
24767                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24768                 
24769                 break;
24770             case 38: // up
24771             case 40: // down
24772                 
24773                 dir = e.keyCode == 38 ? -1 : 1;
24774                 
24775                 this.vIndex = this.vIndex + dir * 4;
24776                 
24777                 if(this.vIndex < 0){
24778                     this.vIndex = 0;
24779                 }
24780                 
24781                 if(this.vIndex > 11){
24782                     this.vIndex = 11;
24783                 }
24784                 
24785                 if(isNaN(this.vIndex)){
24786                     this.vIndex = 0;
24787                 }
24788                 
24789                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24790                 break;
24791                 
24792             case 13: // enter
24793                 
24794                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24795                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24796                 }
24797                 
24798                 this.hide();
24799                 e.preventDefault();
24800                 break;
24801             case 9: // tab
24802                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24803                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24804                 }
24805                 this.hide();
24806                 break;
24807             case 16: // shift
24808             case 17: // ctrl
24809             case 18: // alt
24810                 break;
24811             default :
24812                 this.hide();
24813                 
24814         }
24815     },
24816     
24817     remove: function() 
24818     {
24819         this.picker().remove();
24820     }
24821    
24822 });
24823
24824 Roo.apply(Roo.bootstrap.form.MonthField,  {
24825     
24826     content : {
24827         tag: 'tbody',
24828         cn: [
24829         {
24830             tag: 'tr',
24831             cn: [
24832             {
24833                 tag: 'td',
24834                 colspan: '7'
24835             }
24836             ]
24837         }
24838         ]
24839     },
24840     
24841     dates:{
24842         en: {
24843             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24844             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24845         }
24846     }
24847 });
24848
24849 Roo.apply(Roo.bootstrap.form.MonthField,  {
24850   
24851     template : {
24852         tag: 'div',
24853         cls: 'datepicker dropdown-menu roo-dynamic',
24854         cn: [
24855             {
24856                 tag: 'div',
24857                 cls: 'datepicker-months',
24858                 cn: [
24859                 {
24860                     tag: 'table',
24861                     cls: 'table-condensed',
24862                     cn:[
24863                         Roo.bootstrap.form.DateField.content
24864                     ]
24865                 }
24866                 ]
24867             }
24868         ]
24869     }
24870 });
24871
24872  
24873
24874  
24875  /*
24876  * - LGPL
24877  *
24878  * CheckBox
24879  * 
24880  */
24881
24882 /**
24883  * @class Roo.bootstrap.form.CheckBox
24884  * @extends Roo.bootstrap.form.Input
24885  * Bootstrap CheckBox class
24886  * 
24887  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24888  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24889  * @cfg {String} boxLabel The text that appears beside the checkbox
24890  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24891  * @cfg {Boolean} checked initnal the element
24892  * @cfg {Boolean} inline inline the element (default false)
24893  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24894  * @cfg {String} tooltip label tooltip
24895  * 
24896  * @constructor
24897  * Create a new CheckBox
24898  * @param {Object} config The config object
24899  */
24900
24901 Roo.bootstrap.form.CheckBox = function(config){
24902     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24903    
24904     this.addEvents({
24905         /**
24906         * @event check
24907         * Fires when the element is checked or unchecked.
24908         * @param {Roo.bootstrap.form.CheckBox} this This input
24909         * @param {Boolean} checked The new checked value
24910         */
24911        check : true,
24912        /**
24913         * @event click
24914         * Fires when the element is click.
24915         * @param {Roo.bootstrap.form.CheckBox} this This input
24916         */
24917        click : true
24918     });
24919     
24920 };
24921
24922 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24923   
24924     inputType: 'checkbox',
24925     inputValue: 1,
24926     valueOff: 0,
24927     boxLabel: false,
24928     checked: false,
24929     weight : false,
24930     inline: false,
24931     tooltip : '',
24932     
24933     // checkbox success does not make any sense really.. 
24934     invalidClass : "",
24935     validClass : "",
24936     
24937     
24938     getAutoCreate : function()
24939     {
24940         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24941         
24942         var id = Roo.id();
24943         
24944         var cfg = {};
24945         
24946         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24947         
24948         if(this.inline){
24949             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24950         }
24951         
24952         var input =  {
24953             tag: 'input',
24954             id : id,
24955             type : this.inputType,
24956             value : this.inputValue,
24957             cls : 'roo-' + this.inputType, //'form-box',
24958             placeholder : this.placeholder || ''
24959             
24960         };
24961         
24962         if(this.inputType != 'radio'){
24963             var hidden =  {
24964                 tag: 'input',
24965                 type : 'hidden',
24966                 cls : 'roo-hidden-value',
24967                 value : this.checked ? this.inputValue : this.valueOff
24968             };
24969         }
24970         
24971             
24972         if (this.weight) { // Validity check?
24973             cfg.cls += " " + this.inputType + "-" + this.weight;
24974         }
24975         
24976         if (this.disabled) {
24977             input.disabled=true;
24978         }
24979         
24980         if(this.checked){
24981             input.checked = this.checked;
24982         }
24983         
24984         if (this.name) {
24985             
24986             input.name = this.name;
24987             
24988             if(this.inputType != 'radio'){
24989                 hidden.name = this.name;
24990                 input.name = '_hidden_' + this.name;
24991             }
24992         }
24993         
24994         if (this.size) {
24995             input.cls += ' input-' + this.size;
24996         }
24997         
24998         var settings=this;
24999         
25000         ['xs','sm','md','lg'].map(function(size){
25001             if (settings[size]) {
25002                 cfg.cls += ' col-' + size + '-' + settings[size];
25003             }
25004         });
25005         
25006         var inputblock = input;
25007          
25008         if (this.before || this.after) {
25009             
25010             inputblock = {
25011                 cls : 'input-group',
25012                 cn :  [] 
25013             };
25014             
25015             if (this.before) {
25016                 inputblock.cn.push({
25017                     tag :'span',
25018                     cls : 'input-group-addon',
25019                     html : this.before
25020                 });
25021             }
25022             
25023             inputblock.cn.push(input);
25024             
25025             if(this.inputType != 'radio'){
25026                 inputblock.cn.push(hidden);
25027             }
25028             
25029             if (this.after) {
25030                 inputblock.cn.push({
25031                     tag :'span',
25032                     cls : 'input-group-addon',
25033                     html : this.after
25034                 });
25035             }
25036             
25037         }
25038         var boxLabelCfg = false;
25039         
25040         if(this.boxLabel){
25041            
25042             boxLabelCfg = {
25043                 tag: 'label',
25044                 //'for': id, // box label is handled by onclick - so no for...
25045                 cls: 'box-label',
25046                 html: this.boxLabel
25047             };
25048             if(this.tooltip){
25049                 boxLabelCfg.tooltip = this.tooltip;
25050             }
25051              
25052         }
25053         
25054         
25055         if (align ==='left' && this.fieldLabel.length) {
25056 //                Roo.log("left and has label");
25057             cfg.cn = [
25058                 {
25059                     tag: 'label',
25060                     'for' :  id,
25061                     cls : 'control-label',
25062                     html : this.fieldLabel
25063                 },
25064                 {
25065                     cls : "", 
25066                     cn: [
25067                         inputblock
25068                     ]
25069                 }
25070             ];
25071             
25072             if (boxLabelCfg) {
25073                 cfg.cn[1].cn.push(boxLabelCfg);
25074             }
25075             
25076             if(this.labelWidth > 12){
25077                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25078             }
25079             
25080             if(this.labelWidth < 13 && this.labelmd == 0){
25081                 this.labelmd = this.labelWidth;
25082             }
25083             
25084             if(this.labellg > 0){
25085                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25086                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25087             }
25088             
25089             if(this.labelmd > 0){
25090                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25091                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25092             }
25093             
25094             if(this.labelsm > 0){
25095                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25096                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25097             }
25098             
25099             if(this.labelxs > 0){
25100                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25101                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25102             }
25103             
25104         } else if ( this.fieldLabel.length) {
25105 //                Roo.log(" label");
25106                 cfg.cn = [
25107                    
25108                     {
25109                         tag: this.boxLabel ? 'span' : 'label',
25110                         'for': id,
25111                         cls: 'control-label box-input-label',
25112                         //cls : 'input-group-addon',
25113                         html : this.fieldLabel
25114                     },
25115                     
25116                     inputblock
25117                     
25118                 ];
25119                 if (boxLabelCfg) {
25120                     cfg.cn.push(boxLabelCfg);
25121                 }
25122
25123         } else {
25124             
25125 //                Roo.log(" no label && no align");
25126                 cfg.cn = [  inputblock ] ;
25127                 if (boxLabelCfg) {
25128                     cfg.cn.push(boxLabelCfg);
25129                 }
25130
25131                 
25132         }
25133         
25134        
25135         
25136         if(this.inputType != 'radio'){
25137             cfg.cn.push(hidden);
25138         }
25139         
25140         return cfg;
25141         
25142     },
25143     
25144     /**
25145      * return the real input element.
25146      */
25147     inputEl: function ()
25148     {
25149         return this.el.select('input.roo-' + this.inputType,true).first();
25150     },
25151     hiddenEl: function ()
25152     {
25153         return this.el.select('input.roo-hidden-value',true).first();
25154     },
25155     
25156     labelEl: function()
25157     {
25158         return this.el.select('label.control-label',true).first();
25159     },
25160     /* depricated... */
25161     
25162     label: function()
25163     {
25164         return this.labelEl();
25165     },
25166     
25167     boxLabelEl: function()
25168     {
25169         return this.el.select('label.box-label',true).first();
25170     },
25171     
25172     initEvents : function()
25173     {
25174 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25175         
25176         this.inputEl().on('click', this.onClick,  this);
25177         
25178         if (this.boxLabel) { 
25179             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25180         }
25181         
25182         this.startValue = this.getValue();
25183         
25184         if(this.groupId){
25185             Roo.bootstrap.form.CheckBox.register(this);
25186         }
25187     },
25188     
25189     onClick : function(e)
25190     {   
25191         if(this.fireEvent('click', this, e) !== false){
25192             this.setChecked(!this.checked);
25193         }
25194         
25195     },
25196     
25197     setChecked : function(state,suppressEvent)
25198     {
25199         this.startValue = this.getValue();
25200
25201         if(this.inputType == 'radio'){
25202             
25203             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25204                 e.dom.checked = false;
25205             });
25206             
25207             this.inputEl().dom.checked = true;
25208             
25209             this.inputEl().dom.value = this.inputValue;
25210             
25211             if(suppressEvent !== true){
25212                 this.fireEvent('check', this, true);
25213             }
25214             
25215             this.validate();
25216             
25217             return;
25218         }
25219         
25220         this.checked = state;
25221         
25222         this.inputEl().dom.checked = state;
25223         
25224         
25225         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25226         
25227         if(suppressEvent !== true){
25228             this.fireEvent('check', this, state);
25229         }
25230         
25231         this.validate();
25232     },
25233     
25234     getValue : function()
25235     {
25236         if(this.inputType == 'radio'){
25237             return this.getGroupValue();
25238         }
25239         
25240         return this.hiddenEl().dom.value;
25241         
25242     },
25243     
25244     getGroupValue : function()
25245     {
25246         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25247             return '';
25248         }
25249         
25250         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25251     },
25252     
25253     setValue : function(v,suppressEvent)
25254     {
25255         if(this.inputType == 'radio'){
25256             this.setGroupValue(v, suppressEvent);
25257             return;
25258         }
25259         
25260         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25261         
25262         this.validate();
25263     },
25264     
25265     setGroupValue : function(v, suppressEvent)
25266     {
25267         this.startValue = this.getValue();
25268         
25269         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25270             e.dom.checked = false;
25271             
25272             if(e.dom.value == v){
25273                 e.dom.checked = true;
25274             }
25275         });
25276         
25277         if(suppressEvent !== true){
25278             this.fireEvent('check', this, true);
25279         }
25280
25281         this.validate();
25282         
25283         return;
25284     },
25285     
25286     validate : function()
25287     {
25288         if(this.getVisibilityEl().hasClass('hidden')){
25289             return true;
25290         }
25291         
25292         if(
25293                 this.disabled || 
25294                 (this.inputType == 'radio' && this.validateRadio()) ||
25295                 (this.inputType == 'checkbox' && this.validateCheckbox())
25296         ){
25297             this.markValid();
25298             return true;
25299         }
25300         
25301         this.markInvalid();
25302         return false;
25303     },
25304     
25305     validateRadio : function()
25306     {
25307         if(this.getVisibilityEl().hasClass('hidden')){
25308             return true;
25309         }
25310         
25311         if(this.allowBlank){
25312             return true;
25313         }
25314         
25315         var valid = false;
25316         
25317         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25318             if(!e.dom.checked){
25319                 return;
25320             }
25321             
25322             valid = true;
25323             
25324             return false;
25325         });
25326         
25327         return valid;
25328     },
25329     
25330     validateCheckbox : function()
25331     {
25332         if(!this.groupId){
25333             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25334             //return (this.getValue() == this.inputValue) ? true : false;
25335         }
25336         
25337         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25338         
25339         if(!group){
25340             return false;
25341         }
25342         
25343         var r = false;
25344         
25345         for(var i in group){
25346             if(group[i].el.isVisible(true)){
25347                 r = false;
25348                 break;
25349             }
25350             
25351             r = true;
25352         }
25353         
25354         for(var i in group){
25355             if(r){
25356                 break;
25357             }
25358             
25359             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25360         }
25361         
25362         return r;
25363     },
25364     
25365     /**
25366      * Mark this field as valid
25367      */
25368     markValid : function()
25369     {
25370         var _this = this;
25371         
25372         this.fireEvent('valid', this);
25373         
25374         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25375         
25376         if(this.groupId){
25377             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25378         }
25379         
25380         if(label){
25381             label.markValid();
25382         }
25383
25384         if(this.inputType == 'radio'){
25385             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25386                 var fg = e.findParent('.form-group', false, true);
25387                 if (Roo.bootstrap.version == 3) {
25388                     fg.removeClass([_this.invalidClass, _this.validClass]);
25389                     fg.addClass(_this.validClass);
25390                 } else {
25391                     fg.removeClass(['is-valid', 'is-invalid']);
25392                     fg.addClass('is-valid');
25393                 }
25394             });
25395             
25396             return;
25397         }
25398
25399         if(!this.groupId){
25400             var fg = this.el.findParent('.form-group', false, true);
25401             if (Roo.bootstrap.version == 3) {
25402                 fg.removeClass([this.invalidClass, this.validClass]);
25403                 fg.addClass(this.validClass);
25404             } else {
25405                 fg.removeClass(['is-valid', 'is-invalid']);
25406                 fg.addClass('is-valid');
25407             }
25408             return;
25409         }
25410         
25411         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25412         
25413         if(!group){
25414             return;
25415         }
25416         
25417         for(var i in group){
25418             var fg = group[i].el.findParent('.form-group', false, true);
25419             if (Roo.bootstrap.version == 3) {
25420                 fg.removeClass([this.invalidClass, this.validClass]);
25421                 fg.addClass(this.validClass);
25422             } else {
25423                 fg.removeClass(['is-valid', 'is-invalid']);
25424                 fg.addClass('is-valid');
25425             }
25426         }
25427     },
25428     
25429      /**
25430      * Mark this field as invalid
25431      * @param {String} msg The validation message
25432      */
25433     markInvalid : function(msg)
25434     {
25435         if(this.allowBlank){
25436             return;
25437         }
25438         
25439         var _this = this;
25440         
25441         this.fireEvent('invalid', this, msg);
25442         
25443         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25444         
25445         if(this.groupId){
25446             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25447         }
25448         
25449         if(label){
25450             label.markInvalid();
25451         }
25452             
25453         if(this.inputType == 'radio'){
25454             
25455             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25456                 var fg = e.findParent('.form-group', false, true);
25457                 if (Roo.bootstrap.version == 3) {
25458                     fg.removeClass([_this.invalidClass, _this.validClass]);
25459                     fg.addClass(_this.invalidClass);
25460                 } else {
25461                     fg.removeClass(['is-invalid', 'is-valid']);
25462                     fg.addClass('is-invalid');
25463                 }
25464             });
25465             
25466             return;
25467         }
25468         
25469         if(!this.groupId){
25470             var fg = this.el.findParent('.form-group', false, true);
25471             if (Roo.bootstrap.version == 3) {
25472                 fg.removeClass([_this.invalidClass, _this.validClass]);
25473                 fg.addClass(_this.invalidClass);
25474             } else {
25475                 fg.removeClass(['is-invalid', 'is-valid']);
25476                 fg.addClass('is-invalid');
25477             }
25478             return;
25479         }
25480         
25481         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25482         
25483         if(!group){
25484             return;
25485         }
25486         
25487         for(var i in group){
25488             var fg = group[i].el.findParent('.form-group', false, true);
25489             if (Roo.bootstrap.version == 3) {
25490                 fg.removeClass([_this.invalidClass, _this.validClass]);
25491                 fg.addClass(_this.invalidClass);
25492             } else {
25493                 fg.removeClass(['is-invalid', 'is-valid']);
25494                 fg.addClass('is-invalid');
25495             }
25496         }
25497         
25498     },
25499     
25500     clearInvalid : function()
25501     {
25502         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25503         
25504         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25505         
25506         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25507         
25508         if (label && label.iconEl) {
25509             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25510             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25511         }
25512     },
25513     
25514     disable : function()
25515     {
25516         if(this.inputType != 'radio'){
25517             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25518             return;
25519         }
25520         
25521         var _this = this;
25522         
25523         if(this.rendered){
25524             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25525                 _this.getActionEl().addClass(this.disabledClass);
25526                 e.dom.disabled = true;
25527             });
25528         }
25529         
25530         this.disabled = true;
25531         this.fireEvent("disable", this);
25532         return this;
25533     },
25534
25535     enable : function()
25536     {
25537         if(this.inputType != 'radio'){
25538             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25539             return;
25540         }
25541         
25542         var _this = this;
25543         
25544         if(this.rendered){
25545             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25546                 _this.getActionEl().removeClass(this.disabledClass);
25547                 e.dom.disabled = false;
25548             });
25549         }
25550         
25551         this.disabled = false;
25552         this.fireEvent("enable", this);
25553         return this;
25554     },
25555     
25556     setBoxLabel : function(v)
25557     {
25558         this.boxLabel = v;
25559         
25560         if(this.rendered){
25561             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25562         }
25563     }
25564
25565 });
25566
25567 Roo.apply(Roo.bootstrap.form.CheckBox, {
25568     
25569     groups: {},
25570     
25571      /**
25572     * register a CheckBox Group
25573     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25574     */
25575     register : function(checkbox)
25576     {
25577         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25578             this.groups[checkbox.groupId] = {};
25579         }
25580         
25581         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25582             return;
25583         }
25584         
25585         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25586         
25587     },
25588     /**
25589     * fetch a CheckBox Group based on the group ID
25590     * @param {string} the group ID
25591     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25592     */
25593     get: function(groupId) {
25594         if (typeof(this.groups[groupId]) == 'undefined') {
25595             return false;
25596         }
25597         
25598         return this.groups[groupId] ;
25599     }
25600     
25601     
25602 });
25603 /*
25604  * - LGPL
25605  *
25606  * RadioItem
25607  * 
25608  */
25609
25610 /**
25611  * @class Roo.bootstrap.form.Radio
25612  * @extends Roo.bootstrap.Component
25613  * Bootstrap Radio class
25614  * @cfg {String} boxLabel - the label associated
25615  * @cfg {String} value - the value of radio
25616  * 
25617  * @constructor
25618  * Create a new Radio
25619  * @param {Object} config The config object
25620  */
25621 Roo.bootstrap.form.Radio = function(config){
25622     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25623     
25624 };
25625
25626 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25627     
25628     boxLabel : '',
25629     
25630     value : '',
25631     
25632     getAutoCreate : function()
25633     {
25634         var cfg = {
25635             tag : 'div',
25636             cls : 'form-group radio',
25637             cn : [
25638                 {
25639                     tag : 'label',
25640                     cls : 'box-label',
25641                     html : this.boxLabel
25642                 }
25643             ]
25644         };
25645         
25646         return cfg;
25647     },
25648     
25649     initEvents : function() 
25650     {
25651         this.parent().register(this);
25652         
25653         this.el.on('click', this.onClick, this);
25654         
25655     },
25656     
25657     onClick : function(e)
25658     {
25659         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25660             this.setChecked(true);
25661         }
25662     },
25663     
25664     setChecked : function(state, suppressEvent)
25665     {
25666         this.parent().setValue(this.value, suppressEvent);
25667         
25668     },
25669     
25670     setBoxLabel : function(v)
25671     {
25672         this.boxLabel = v;
25673         
25674         if(this.rendered){
25675             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25676         }
25677     }
25678     
25679 });
25680  
25681
25682  /*
25683  * - LGPL
25684  *
25685  * Input
25686  * 
25687  */
25688
25689 /**
25690  * @class Roo.bootstrap.form.SecurePass
25691  * @extends Roo.bootstrap.form.Input
25692  * Bootstrap SecurePass class
25693  *
25694  * 
25695  * @constructor
25696  * Create a new SecurePass
25697  * @param {Object} config The config object
25698  */
25699  
25700 Roo.bootstrap.form.SecurePass = function (config) {
25701     // these go here, so the translation tool can replace them..
25702     this.errors = {
25703         PwdEmpty: "Please type a password, and then retype it to confirm.",
25704         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25705         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25706         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25707         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25708         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25709         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25710         TooWeak: "Your password is Too Weak."
25711     },
25712     this.meterLabel = "Password strength:";
25713     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25714     this.meterClass = [
25715         "roo-password-meter-tooweak", 
25716         "roo-password-meter-weak", 
25717         "roo-password-meter-medium", 
25718         "roo-password-meter-strong", 
25719         "roo-password-meter-grey"
25720     ];
25721     
25722     this.errors = {};
25723     
25724     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25725 }
25726
25727 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25728     /**
25729      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25730      * {
25731      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25732      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25733      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25734      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25735      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25736      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25737      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25738      * })
25739      */
25740     // private
25741     
25742     meterWidth: 300,
25743     errorMsg :'',    
25744     errors: false,
25745     imageRoot: '/',
25746     /**
25747      * @cfg {String/Object} Label for the strength meter (defaults to
25748      * 'Password strength:')
25749      */
25750     // private
25751     meterLabel: '',
25752     /**
25753      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25754      * ['Weak', 'Medium', 'Strong'])
25755      */
25756     // private    
25757     pwdStrengths: false,    
25758     // private
25759     strength: 0,
25760     // private
25761     _lastPwd: null,
25762     // private
25763     kCapitalLetter: 0,
25764     kSmallLetter: 1,
25765     kDigit: 2,
25766     kPunctuation: 3,
25767     
25768     insecure: false,
25769     // private
25770     initEvents: function ()
25771     {
25772         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25773
25774         if (this.el.is('input[type=password]') && Roo.isSafari) {
25775             this.el.on('keydown', this.SafariOnKeyDown, this);
25776         }
25777
25778         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25779     },
25780     // private
25781     onRender: function (ct, position)
25782     {
25783         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25784         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25785         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25786
25787         this.trigger.createChild({
25788                    cn: [
25789                     {
25790                     //id: 'PwdMeter',
25791                     tag: 'div',
25792                     cls: 'roo-password-meter-grey col-xs-12',
25793                     style: {
25794                         //width: 0,
25795                         //width: this.meterWidth + 'px'                                                
25796                         }
25797                     },
25798                     {                            
25799                          cls: 'roo-password-meter-text'                          
25800                     }
25801                 ]            
25802         });
25803
25804          
25805         if (this.hideTrigger) {
25806             this.trigger.setDisplayed(false);
25807         }
25808         this.setSize(this.width || '', this.height || '');
25809     },
25810     // private
25811     onDestroy: function ()
25812     {
25813         if (this.trigger) {
25814             this.trigger.removeAllListeners();
25815             this.trigger.remove();
25816         }
25817         if (this.wrap) {
25818             this.wrap.remove();
25819         }
25820         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25821     },
25822     // private
25823     checkStrength: function ()
25824     {
25825         var pwd = this.inputEl().getValue();
25826         if (pwd == this._lastPwd) {
25827             return;
25828         }
25829
25830         var strength;
25831         if (this.ClientSideStrongPassword(pwd)) {
25832             strength = 3;
25833         } else if (this.ClientSideMediumPassword(pwd)) {
25834             strength = 2;
25835         } else if (this.ClientSideWeakPassword(pwd)) {
25836             strength = 1;
25837         } else {
25838             strength = 0;
25839         }
25840         
25841         Roo.log('strength1: ' + strength);
25842         
25843         //var pm = this.trigger.child('div/div/div').dom;
25844         var pm = this.trigger.child('div/div');
25845         pm.removeClass(this.meterClass);
25846         pm.addClass(this.meterClass[strength]);
25847                 
25848         
25849         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25850                 
25851         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25852         
25853         this._lastPwd = pwd;
25854     },
25855     reset: function ()
25856     {
25857         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25858         
25859         this._lastPwd = '';
25860         
25861         var pm = this.trigger.child('div/div');
25862         pm.removeClass(this.meterClass);
25863         pm.addClass('roo-password-meter-grey');        
25864         
25865         
25866         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25867         
25868         pt.innerHTML = '';
25869         this.inputEl().dom.type='password';
25870     },
25871     // private
25872     validateValue: function (value)
25873     {
25874         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25875             return false;
25876         }
25877         if (value.length == 0) {
25878             if (this.allowBlank) {
25879                 this.clearInvalid();
25880                 return true;
25881             }
25882
25883             this.markInvalid(this.errors.PwdEmpty);
25884             this.errorMsg = this.errors.PwdEmpty;
25885             return false;
25886         }
25887         
25888         if(this.insecure){
25889             return true;
25890         }
25891         
25892         if (!value.match(/[\x21-\x7e]+/)) {
25893             this.markInvalid(this.errors.PwdBadChar);
25894             this.errorMsg = this.errors.PwdBadChar;
25895             return false;
25896         }
25897         if (value.length < 6) {
25898             this.markInvalid(this.errors.PwdShort);
25899             this.errorMsg = this.errors.PwdShort;
25900             return false;
25901         }
25902         if (value.length > 16) {
25903             this.markInvalid(this.errors.PwdLong);
25904             this.errorMsg = this.errors.PwdLong;
25905             return false;
25906         }
25907         var strength;
25908         if (this.ClientSideStrongPassword(value)) {
25909             strength = 3;
25910         } else if (this.ClientSideMediumPassword(value)) {
25911             strength = 2;
25912         } else if (this.ClientSideWeakPassword(value)) {
25913             strength = 1;
25914         } else {
25915             strength = 0;
25916         }
25917
25918         
25919         if (strength < 2) {
25920             //this.markInvalid(this.errors.TooWeak);
25921             this.errorMsg = this.errors.TooWeak;
25922             //return false;
25923         }
25924         
25925         
25926         console.log('strength2: ' + strength);
25927         
25928         //var pm = this.trigger.child('div/div/div').dom;
25929         
25930         var pm = this.trigger.child('div/div');
25931         pm.removeClass(this.meterClass);
25932         pm.addClass(this.meterClass[strength]);
25933                 
25934         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25935                 
25936         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25937         
25938         this.errorMsg = ''; 
25939         return true;
25940     },
25941     // private
25942     CharacterSetChecks: function (type)
25943     {
25944         this.type = type;
25945         this.fResult = false;
25946     },
25947     // private
25948     isctype: function (character, type)
25949     {
25950         switch (type) {  
25951             case this.kCapitalLetter:
25952                 if (character >= 'A' && character <= 'Z') {
25953                     return true;
25954                 }
25955                 break;
25956             
25957             case this.kSmallLetter:
25958                 if (character >= 'a' && character <= 'z') {
25959                     return true;
25960                 }
25961                 break;
25962             
25963             case this.kDigit:
25964                 if (character >= '0' && character <= '9') {
25965                     return true;
25966                 }
25967                 break;
25968             
25969             case this.kPunctuation:
25970                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25971                     return true;
25972                 }
25973                 break;
25974             
25975             default:
25976                 return false;
25977         }
25978
25979     },
25980     // private
25981     IsLongEnough: function (pwd, size)
25982     {
25983         return !(pwd == null || isNaN(size) || pwd.length < size);
25984     },
25985     // private
25986     SpansEnoughCharacterSets: function (word, nb)
25987     {
25988         if (!this.IsLongEnough(word, nb))
25989         {
25990             return false;
25991         }
25992
25993         var characterSetChecks = new Array(
25994             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25995             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25996         );
25997         
25998         for (var index = 0; index < word.length; ++index) {
25999             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26000                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
26001                     characterSetChecks[nCharSet].fResult = true;
26002                     break;
26003                 }
26004             }
26005         }
26006
26007         var nCharSets = 0;
26008         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26009             if (characterSetChecks[nCharSet].fResult) {
26010                 ++nCharSets;
26011             }
26012         }
26013
26014         if (nCharSets < nb) {
26015             return false;
26016         }
26017         return true;
26018     },
26019     // private
26020     ClientSideStrongPassword: function (pwd)
26021     {
26022         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26023     },
26024     // private
26025     ClientSideMediumPassword: function (pwd)
26026     {
26027         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26028     },
26029     // private
26030     ClientSideWeakPassword: function (pwd)
26031     {
26032         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26033     }
26034           
26035 });
26036 Roo.htmleditor = {};
26037  
26038 /**
26039  * @class Roo.htmleditor.Filter
26040  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26041  * @cfg {DomElement} node The node to iterate and filter
26042  * @cfg {boolean|String|Array} tag Tags to replace 
26043  * @constructor
26044  * Create a new Filter.
26045  * @param {Object} config Configuration options
26046  */
26047
26048
26049
26050 Roo.htmleditor.Filter = function(cfg) {
26051     Roo.apply(this.cfg);
26052     // this does not actually call walk as it's really just a abstract class
26053 }
26054
26055
26056 Roo.htmleditor.Filter.prototype = {
26057     
26058     node: false,
26059     
26060     tag: false,
26061
26062     // overrride to do replace comments.
26063     replaceComment : false,
26064     
26065     // overrride to do replace or do stuff with tags..
26066     replaceTag : false,
26067     
26068     walk : function(dom)
26069     {
26070         Roo.each( Array.from(dom.childNodes), function( e ) {
26071             switch(true) {
26072                 
26073                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26074                     this.replaceComment(e);
26075                     return;
26076                 
26077                 case e.nodeType != 1: //not a node.
26078                     return;
26079                 
26080                 case this.tag === true: // everything
26081                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26082                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26083                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26084                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26085                     if (this.replaceTag && false === this.replaceTag(e)) {
26086                         return;
26087                     }
26088                     if (e.hasChildNodes()) {
26089                         this.walk(e);
26090                     }
26091                     return;
26092                 
26093                 default:    // tags .. that do not match.
26094                     if (e.hasChildNodes()) {
26095                         this.walk(e);
26096                     }
26097             }
26098             
26099         }, this);
26100         
26101     },
26102     
26103     
26104     removeNodeKeepChildren : function( node)
26105     {
26106     
26107         ar = Array.from(node.childNodes);
26108         for (var i = 0; i < ar.length; i++) {
26109          
26110             node.removeChild(ar[i]);
26111             // what if we need to walk these???
26112             node.parentNode.insertBefore(ar[i], node);
26113            
26114         }
26115         node.parentNode.removeChild(node);
26116     }
26117 }; 
26118
26119 /**
26120  * @class Roo.htmleditor.FilterAttributes
26121  * clean attributes and  styles including http:// etc.. in attribute
26122  * @constructor
26123 * Run a new Attribute Filter
26124 * @param {Object} config Configuration options
26125  */
26126 Roo.htmleditor.FilterAttributes = function(cfg)
26127 {
26128     Roo.apply(this, cfg);
26129     this.attrib_black = this.attrib_black || [];
26130     this.attrib_white = this.attrib_white || [];
26131
26132     this.attrib_clean = this.attrib_clean || [];
26133     this.style_white = this.style_white || [];
26134     this.style_black = this.style_black || [];
26135     this.walk(cfg.node);
26136 }
26137
26138 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26139 {
26140     tag: true, // all tags
26141     
26142     attrib_black : false, // array
26143     attrib_clean : false,
26144     attrib_white : false,
26145
26146     style_white : false,
26147     style_black : false,
26148      
26149      
26150     replaceTag : function(node)
26151     {
26152         if (!node.attributes || !node.attributes.length) {
26153             return true;
26154         }
26155         
26156         for (var i = node.attributes.length-1; i > -1 ; i--) {
26157             var a = node.attributes[i];
26158             //console.log(a);
26159             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26160                 node.removeAttribute(a.name);
26161                 continue;
26162             }
26163             
26164             
26165             
26166             if (a.name.toLowerCase().substr(0,2)=='on')  {
26167                 node.removeAttribute(a.name);
26168                 continue;
26169             }
26170             
26171             
26172             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26173                 node.removeAttribute(a.name);
26174                 continue;
26175             }
26176             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26177                 this.cleanAttr(node,a.name,a.value); // fixme..
26178                 continue;
26179             }
26180             if (a.name == 'style') {
26181                 this.cleanStyle(node,a.name,a.value);
26182                 continue;
26183             }
26184             /// clean up MS crap..
26185             // tecnically this should be a list of valid class'es..
26186             
26187             
26188             if (a.name == 'class') {
26189                 if (a.value.match(/^Mso/)) {
26190                     node.removeAttribute('class');
26191                 }
26192                 
26193                 if (a.value.match(/^body$/)) {
26194                     node.removeAttribute('class');
26195                 }
26196                 continue;
26197             }
26198             
26199             
26200             // style cleanup!?
26201             // class cleanup?
26202             
26203         }
26204         return true; // clean children
26205     },
26206         
26207     cleanAttr: function(node, n,v)
26208     {
26209         
26210         if (v.match(/^\./) || v.match(/^\//)) {
26211             return;
26212         }
26213         if (v.match(/^(http|https):\/\//)
26214             || v.match(/^mailto:/) 
26215             || v.match(/^ftp:/)
26216             || v.match(/^data:/)
26217             ) {
26218             return;
26219         }
26220         if (v.match(/^#/)) {
26221             return;
26222         }
26223         if (v.match(/^\{/)) { // allow template editing.
26224             return;
26225         }
26226 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26227         node.removeAttribute(n);
26228         
26229     },
26230     cleanStyle : function(node,  n,v)
26231     {
26232         if (v.match(/expression/)) { //XSS?? should we even bother..
26233             node.removeAttribute(n);
26234             return;
26235         }
26236         
26237         var parts = v.split(/;/);
26238         var clean = [];
26239         
26240         Roo.each(parts, function(p) {
26241             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26242             if (!p.length) {
26243                 return true;
26244             }
26245             var l = p.split(':').shift().replace(/\s+/g,'');
26246             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26247             
26248             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26249                 return true;
26250             }
26251             //Roo.log()
26252             // only allow 'c whitelisted system attributes'
26253             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26254                 return true;
26255             }
26256             
26257             
26258             clean.push(p);
26259             return true;
26260         },this);
26261         if (clean.length) { 
26262             node.setAttribute(n, clean.join(';'));
26263         } else {
26264             node.removeAttribute(n);
26265         }
26266         
26267     }
26268         
26269         
26270         
26271     
26272 });/**
26273  * @class Roo.htmleditor.FilterBlack
26274  * remove blacklisted elements.
26275  * @constructor
26276  * Run a new Blacklisted Filter
26277  * @param {Object} config Configuration options
26278  */
26279
26280 Roo.htmleditor.FilterBlack = function(cfg)
26281 {
26282     Roo.apply(this, cfg);
26283     this.walk(cfg.node);
26284 }
26285
26286 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26287 {
26288     tag : true, // all elements.
26289    
26290     replaceTag : function(n)
26291     {
26292         n.parentNode.removeChild(n);
26293     }
26294 });
26295 /**
26296  * @class Roo.htmleditor.FilterComment
26297  * remove comments.
26298  * @constructor
26299 * Run a new Comments Filter
26300 * @param {Object} config Configuration options
26301  */
26302 Roo.htmleditor.FilterComment = function(cfg)
26303 {
26304     this.walk(cfg.node);
26305 }
26306
26307 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26308 {
26309   
26310     replaceComment : function(n)
26311     {
26312         n.parentNode.removeChild(n);
26313     }
26314 });/**
26315  * @class Roo.htmleditor.FilterKeepChildren
26316  * remove tags but keep children
26317  * @constructor
26318  * Run a new Keep Children Filter
26319  * @param {Object} config Configuration options
26320  */
26321
26322 Roo.htmleditor.FilterKeepChildren = function(cfg)
26323 {
26324     Roo.apply(this, cfg);
26325     if (this.tag === false) {
26326         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26327     }
26328     // hacky?
26329     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26330         this.cleanNamespace = true;
26331     }
26332         
26333     this.walk(cfg.node);
26334 }
26335
26336 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26337 {
26338     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26339   
26340     replaceTag : function(node)
26341     {
26342         // walk children...
26343         //Roo.log(node.tagName);
26344         var ar = Array.from(node.childNodes);
26345         //remove first..
26346         
26347         for (var i = 0; i < ar.length; i++) {
26348             var e = ar[i];
26349             if (e.nodeType == 1) {
26350                 if (
26351                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26352                     || // array and it matches
26353                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26354                     ||
26355                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26356                     ||
26357                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26358                 ) {
26359                     this.replaceTag(ar[i]); // child is blacklisted as well...
26360                     continue;
26361                 }
26362             }
26363         }  
26364         ar = Array.from(node.childNodes);
26365         for (var i = 0; i < ar.length; i++) {
26366          
26367             node.removeChild(ar[i]);
26368             // what if we need to walk these???
26369             node.parentNode.insertBefore(ar[i], node);
26370             if (this.tag !== false) {
26371                 this.walk(ar[i]);
26372                 
26373             }
26374         }
26375         //Roo.log("REMOVE:" + node.tagName);
26376         node.parentNode.removeChild(node);
26377         return false; // don't walk children
26378         
26379         
26380     }
26381 });/**
26382  * @class Roo.htmleditor.FilterParagraph
26383  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26384  * like on 'push' to remove the <p> tags and replace them with line breaks.
26385  * @constructor
26386  * Run a new Paragraph Filter
26387  * @param {Object} config Configuration options
26388  */
26389
26390 Roo.htmleditor.FilterParagraph = function(cfg)
26391 {
26392     // no need to apply config.
26393     this.walk(cfg.node);
26394 }
26395
26396 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26397 {
26398     
26399      
26400     tag : 'P',
26401     
26402      
26403     replaceTag : function(node)
26404     {
26405         
26406         if (node.childNodes.length == 1 &&
26407             node.childNodes[0].nodeType == 3 &&
26408             node.childNodes[0].textContent.trim().length < 1
26409             ) {
26410             // remove and replace with '<BR>';
26411             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26412             return false; // no need to walk..
26413         }
26414         var ar = Array.from(node.childNodes);
26415         for (var i = 0; i < ar.length; i++) {
26416             node.removeChild(ar[i]);
26417             // what if we need to walk these???
26418             node.parentNode.insertBefore(ar[i], node);
26419         }
26420         // now what about this?
26421         // <p> &nbsp; </p>
26422         
26423         // double BR.
26424         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26425         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26426         node.parentNode.removeChild(node);
26427         
26428         return false;
26429
26430     }
26431     
26432 });/**
26433  * @class Roo.htmleditor.FilterSpan
26434  * filter span's with no attributes out..
26435  * @constructor
26436  * Run a new Span Filter
26437  * @param {Object} config Configuration options
26438  */
26439
26440 Roo.htmleditor.FilterSpan = function(cfg)
26441 {
26442     // no need to apply config.
26443     this.walk(cfg.node);
26444 }
26445
26446 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26447 {
26448      
26449     tag : 'SPAN',
26450      
26451  
26452     replaceTag : function(node)
26453     {
26454         if (node.attributes && node.attributes.length > 0) {
26455             return true; // walk if there are any.
26456         }
26457         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26458         return false;
26459      
26460     }
26461     
26462 });/**
26463  * @class Roo.htmleditor.FilterTableWidth
26464   try and remove table width data - as that frequently messes up other stuff.
26465  * 
26466  *      was cleanTableWidths.
26467  *
26468  * Quite often pasting from word etc.. results in tables with column and widths.
26469  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26470  *
26471  * @constructor
26472  * Run a new Table Filter
26473  * @param {Object} config Configuration options
26474  */
26475
26476 Roo.htmleditor.FilterTableWidth = function(cfg)
26477 {
26478     // no need to apply config.
26479     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26480     this.walk(cfg.node);
26481 }
26482
26483 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26484 {
26485      
26486      
26487     
26488     replaceTag: function(node) {
26489         
26490         
26491       
26492         if (node.hasAttribute('width')) {
26493             node.removeAttribute('width');
26494         }
26495         
26496          
26497         if (node.hasAttribute("style")) {
26498             // pretty basic...
26499             
26500             var styles = node.getAttribute("style").split(";");
26501             var nstyle = [];
26502             Roo.each(styles, function(s) {
26503                 if (!s.match(/:/)) {
26504                     return;
26505                 }
26506                 var kv = s.split(":");
26507                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26508                     return;
26509                 }
26510                 // what ever is left... we allow.
26511                 nstyle.push(s);
26512             });
26513             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26514             if (!nstyle.length) {
26515                 node.removeAttribute('style');
26516             }
26517         }
26518         
26519         return true; // continue doing children..
26520     }
26521 });/**
26522  * @class Roo.htmleditor.FilterWord
26523  * try and clean up all the mess that Word generates.
26524  * 
26525  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26526  
26527  * @constructor
26528  * Run a new Span Filter
26529  * @param {Object} config Configuration options
26530  */
26531
26532 Roo.htmleditor.FilterWord = function(cfg)
26533 {
26534     // no need to apply config.
26535     this.replaceDocBullets(cfg.node);
26536     
26537     this.replaceAname(cfg.node);
26538     // this is disabled as the removal is done by other filters;
26539    // this.walk(cfg.node);
26540     
26541     
26542 }
26543
26544 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26545 {
26546     tag: true,
26547      
26548     
26549     /**
26550      * Clean up MS wordisms...
26551      */
26552     replaceTag : function(node)
26553     {
26554          
26555         // no idea what this does - span with text, replaceds with just text.
26556         if(
26557                 node.nodeName == 'SPAN' &&
26558                 !node.hasAttributes() &&
26559                 node.childNodes.length == 1 &&
26560                 node.firstChild.nodeName == "#text"  
26561         ) {
26562             var textNode = node.firstChild;
26563             node.removeChild(textNode);
26564             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26565                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26566             }
26567             node.parentNode.insertBefore(textNode, node);
26568             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26569                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26570             }
26571             
26572             node.parentNode.removeChild(node);
26573             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26574         }
26575         
26576    
26577         
26578         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26579             node.parentNode.removeChild(node);
26580             return false; // dont do chidlren
26581         }
26582         //Roo.log(node.tagName);
26583         // remove - but keep children..
26584         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26585             //Roo.log('-- removed');
26586             while (node.childNodes.length) {
26587                 var cn = node.childNodes[0];
26588                 node.removeChild(cn);
26589                 node.parentNode.insertBefore(cn, node);
26590                 // move node to parent - and clean it..
26591                 if (cn.nodeType == 1) {
26592                     this.replaceTag(cn);
26593                 }
26594                 
26595             }
26596             node.parentNode.removeChild(node);
26597             /// no need to iterate chidlren = it's got none..
26598             //this.iterateChildren(node, this.cleanWord);
26599             return false; // no need to iterate children.
26600         }
26601         // clean styles
26602         if (node.className.length) {
26603             
26604             var cn = node.className.split(/\W+/);
26605             var cna = [];
26606             Roo.each(cn, function(cls) {
26607                 if (cls.match(/Mso[a-zA-Z]+/)) {
26608                     return;
26609                 }
26610                 cna.push(cls);
26611             });
26612             node.className = cna.length ? cna.join(' ') : '';
26613             if (!cna.length) {
26614                 node.removeAttribute("class");
26615             }
26616         }
26617         
26618         if (node.hasAttribute("lang")) {
26619             node.removeAttribute("lang");
26620         }
26621         
26622         if (node.hasAttribute("style")) {
26623             
26624             var styles = node.getAttribute("style").split(";");
26625             var nstyle = [];
26626             Roo.each(styles, function(s) {
26627                 if (!s.match(/:/)) {
26628                     return;
26629                 }
26630                 var kv = s.split(":");
26631                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26632                     return;
26633                 }
26634                 // what ever is left... we allow.
26635                 nstyle.push(s);
26636             });
26637             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26638             if (!nstyle.length) {
26639                 node.removeAttribute('style');
26640             }
26641         }
26642         return true; // do children
26643         
26644         
26645         
26646     },
26647     
26648     styleToObject: function(node)
26649     {
26650         var styles = (node.getAttribute("style") || '').split(";");
26651         var ret = {};
26652         Roo.each(styles, function(s) {
26653             if (!s.match(/:/)) {
26654                 return;
26655             }
26656             var kv = s.split(":");
26657              
26658             // what ever is left... we allow.
26659             ret[kv[0].trim()] = kv[1];
26660         });
26661         return ret;
26662     },
26663     
26664     
26665     replaceAname : function (doc)
26666     {
26667         // replace all the a/name without..
26668         var aa = Array.from(doc.getElementsByTagName('a'));
26669         for (var i = 0; i  < aa.length; i++) {
26670             var a = aa[i];
26671             if (a.hasAttribute("name")) {
26672                 a.removeAttribute("name");
26673             }
26674             if (a.hasAttribute("href")) {
26675                 continue;
26676             }
26677             // reparent children.
26678             this.removeNodeKeepChildren(a);
26679             
26680         }
26681         
26682         
26683         
26684     },
26685
26686     
26687     
26688     replaceDocBullets : function(doc)
26689     {
26690         // this is a bit odd - but it appears some indents use ql-indent-1
26691          //Roo.log(doc.innerHTML);
26692         
26693         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26694         for( var i = 0; i < listpara.length; i ++) {
26695             listpara[i].className = "MsoListParagraph";
26696         }
26697         
26698         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26699         for( var i = 0; i < listpara.length; i ++) {
26700             listpara[i].className = "MsoListParagraph";
26701         }
26702         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26703         for( var i = 0; i < listpara.length; i ++) {
26704             listpara[i].className = "MsoListParagraph";
26705         }
26706         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26707         for( var i = 0; i < listpara.length; i ++) {
26708             listpara[i].className = "MsoListParagraph";
26709         }
26710         
26711         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26712         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26713         for( var i = 0; i < htwo.length; i ++) {
26714             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26715                 htwo[i].className = "MsoListParagraph";
26716             }
26717         }
26718         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26719         for( var i = 0; i < listpara.length; i ++) {
26720             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26721                 listpara[i].className = "MsoListParagraph";
26722             } else {
26723                 listpara[i].className = "MsoNormalx";
26724             }
26725         }
26726        
26727         listpara = doc.getElementsByClassName('MsoListParagraph');
26728         // Roo.log(doc.innerHTML);
26729         
26730         
26731         
26732         while(listpara.length) {
26733             
26734             this.replaceDocBullet(listpara.item(0));
26735         }
26736       
26737     },
26738     
26739      
26740     
26741     replaceDocBullet : function(p)
26742     {
26743         // gather all the siblings.
26744         var ns = p,
26745             parent = p.parentNode,
26746             doc = parent.ownerDocument,
26747             items = [];
26748             
26749         var listtype = 'ul';   
26750         while (ns) {
26751             if (ns.nodeType != 1) {
26752                 ns = ns.nextSibling;
26753                 continue;
26754             }
26755             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26756                 break;
26757             }
26758             var spans = ns.getElementsByTagName('span');
26759             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26760                 items.push(ns);
26761                 ns = ns.nextSibling;
26762                 has_list = true;
26763                 if (spans.length && spans[0].hasAttribute('style')) {
26764                     var  style = this.styleToObject(spans[0]);
26765                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26766                         listtype = 'ol';
26767                     }
26768                 }
26769                 
26770                 continue;
26771             }
26772             var spans = ns.getElementsByTagName('span');
26773             if (!spans.length) {
26774                 break;
26775             }
26776             var has_list  = false;
26777             for(var i = 0; i < spans.length; i++) {
26778                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26779                     has_list = true;
26780                     break;
26781                 }
26782             }
26783             if (!has_list) {
26784                 break;
26785             }
26786             items.push(ns);
26787             ns = ns.nextSibling;
26788             
26789             
26790         }
26791         if (!items.length) {
26792             ns.className = "";
26793             return;
26794         }
26795         
26796         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26797         parent.insertBefore(ul, p);
26798         var lvl = 0;
26799         var stack = [ ul ];
26800         var last_li = false;
26801         
26802         var margin_to_depth = {};
26803         max_margins = -1;
26804         
26805         items.forEach(function(n, ipos) {
26806             //Roo.log("got innertHMLT=" + n.innerHTML);
26807             
26808             var spans = n.getElementsByTagName('span');
26809             if (!spans.length) {
26810                 //Roo.log("No spans found");
26811                  
26812                 parent.removeChild(n);
26813                 
26814                 
26815                 return; // skip it...
26816             }
26817            
26818                 
26819             var num = 1;
26820             var style = {};
26821             for(var i = 0; i < spans.length; i++) {
26822             
26823                 style = this.styleToObject(spans[i]);
26824                 if (typeof(style['mso-list']) == 'undefined') {
26825                     continue;
26826                 }
26827                 if (listtype == 'ol') {
26828                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26829                 }
26830                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26831                 break;
26832             }
26833             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26834             style = this.styleToObject(n); // mo-list is from the parent node.
26835             if (typeof(style['mso-list']) == 'undefined') {
26836                 //Roo.log("parent is missing level");
26837                   
26838                 parent.removeChild(n);
26839                  
26840                 return;
26841             }
26842             
26843             var margin = style['margin-left'];
26844             if (typeof(margin_to_depth[margin]) == 'undefined') {
26845                 max_margins++;
26846                 margin_to_depth[margin] = max_margins;
26847             }
26848             nlvl = margin_to_depth[margin] ;
26849              
26850             if (nlvl > lvl) {
26851                 //new indent
26852                 var nul = doc.createElement(listtype); // what about number lists...
26853                 if (!last_li) {
26854                     last_li = doc.createElement('li');
26855                     stack[lvl].appendChild(last_li);
26856                 }
26857                 last_li.appendChild(nul);
26858                 stack[nlvl] = nul;
26859                 
26860             }
26861             lvl = nlvl;
26862             
26863             // not starting at 1..
26864             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26865                 stack[nlvl].setAttribute("start", num);
26866             }
26867             
26868             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26869             last_li = nli;
26870             nli.innerHTML = n.innerHTML;
26871             //Roo.log("innerHTML = " + n.innerHTML);
26872             parent.removeChild(n);
26873             
26874              
26875              
26876             
26877         },this);
26878         
26879         
26880         
26881         
26882     }
26883     
26884     
26885     
26886 });
26887 /**
26888  * @class Roo.htmleditor.FilterStyleToTag
26889  * part of the word stuff... - certain 'styles' should be converted to tags.
26890  * eg.
26891  *   font-weight: bold -> bold
26892  *   ?? super / subscrit etc..
26893  * 
26894  * @constructor
26895 * Run a new style to tag filter.
26896 * @param {Object} config Configuration options
26897  */
26898 Roo.htmleditor.FilterStyleToTag = function(cfg)
26899 {
26900     
26901     this.tags = {
26902         B  : [ 'fontWeight' , 'bold'],
26903         I :  [ 'fontStyle' , 'italic'],
26904         //pre :  [ 'font-style' , 'italic'],
26905         // h1.. h6 ?? font-size?
26906         SUP : [ 'verticalAlign' , 'super' ],
26907         SUB : [ 'verticalAlign' , 'sub' ]
26908         
26909         
26910     };
26911     
26912     Roo.apply(this, cfg);
26913      
26914     
26915     this.walk(cfg.node);
26916     
26917     
26918     
26919 }
26920
26921
26922 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26923 {
26924     tag: true, // all tags
26925     
26926     tags : false,
26927     
26928     
26929     replaceTag : function(node)
26930     {
26931         
26932         
26933         if (node.getAttribute("style") === null) {
26934             return true;
26935         }
26936         var inject = [];
26937         for (var k in this.tags) {
26938             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26939                 inject.push(k);
26940                 node.style.removeProperty(this.tags[k][0]);
26941             }
26942         }
26943         if (!inject.length) {
26944             return true; 
26945         }
26946         var cn = Array.from(node.childNodes);
26947         var nn = node;
26948         Roo.each(inject, function(t) {
26949             var nc = node.ownerDocument.createElement(t);
26950             nn.appendChild(nc);
26951             nn = nc;
26952         });
26953         for(var i = 0;i < cn.length;cn++) {
26954             node.removeChild(cn[i]);
26955             nn.appendChild(cn[i]);
26956         }
26957         return true /// iterate thru
26958     }
26959     
26960 })/**
26961  * @class Roo.htmleditor.FilterLongBr
26962  * BR/BR/BR - keep a maximum of 2...
26963  * @constructor
26964  * Run a new Long BR Filter
26965  * @param {Object} config Configuration options
26966  */
26967
26968 Roo.htmleditor.FilterLongBr = function(cfg)
26969 {
26970     // no need to apply config.
26971     this.walk(cfg.node);
26972 }
26973
26974 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26975 {
26976     
26977      
26978     tag : 'BR',
26979     
26980      
26981     replaceTag : function(node)
26982     {
26983         
26984         var ps = node.nextSibling;
26985         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26986             ps = ps.nextSibling;
26987         }
26988         
26989         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26990             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26991             return false;
26992         }
26993         
26994         if (!ps || ps.nodeType != 1) {
26995             return false;
26996         }
26997         
26998         if (!ps || ps.tagName != 'BR') {
26999            
27000             return false;
27001         }
27002         
27003         
27004         
27005         
27006         
27007         if (!node.previousSibling) {
27008             return false;
27009         }
27010         var ps = node.previousSibling;
27011         
27012         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27013             ps = ps.previousSibling;
27014         }
27015         if (!ps || ps.nodeType != 1) {
27016             return false;
27017         }
27018         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27019         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27020             return false;
27021         }
27022         
27023         node.parentNode.removeChild(node); // remove me...
27024         
27025         return false; // no need to do children
27026
27027     }
27028     
27029 }); 
27030
27031 /**
27032  * @class Roo.htmleditor.FilterBlock
27033  * removes id / data-block and contenteditable that are associated with blocks
27034  * usage should be done on a cloned copy of the dom
27035  * @constructor
27036 * Run a new Attribute Filter { node : xxxx }}
27037 * @param {Object} config Configuration options
27038  */
27039 Roo.htmleditor.FilterBlock = function(cfg)
27040 {
27041     Roo.apply(this, cfg);
27042     var qa = cfg.node.querySelectorAll;
27043     this.removeAttributes('data-block');
27044     this.removeAttributes('contenteditable');
27045     this.removeAttributes('id');
27046     
27047 }
27048
27049 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27050 {
27051     node: true, // all tags
27052      
27053      
27054     removeAttributes : function(attr)
27055     {
27056         var ar = this.node.querySelectorAll('*[' + attr + ']');
27057         for (var i =0;i<ar.length;i++) {
27058             ar[i].removeAttribute(attr);
27059         }
27060     }
27061         
27062         
27063         
27064     
27065 });
27066 /***
27067  * This is based loosely on tinymce 
27068  * @class Roo.htmleditor.TidySerializer
27069  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27070  * @constructor
27071  * @method Serializer
27072  * @param {Object} settings Name/value settings object.
27073  */
27074
27075
27076 Roo.htmleditor.TidySerializer = function(settings)
27077 {
27078     Roo.apply(this, settings);
27079     
27080     this.writer = new Roo.htmleditor.TidyWriter(settings);
27081     
27082     
27083
27084 };
27085 Roo.htmleditor.TidySerializer.prototype = {
27086     
27087     /**
27088      * @param {boolean} inner do the inner of the node.
27089      */
27090     inner : false,
27091     
27092     writer : false,
27093     
27094     /**
27095     * Serializes the specified node into a string.
27096     *
27097     * @example
27098     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27099     * @method serialize
27100     * @param {DomElement} node Node instance to serialize.
27101     * @return {String} String with HTML based on DOM tree.
27102     */
27103     serialize : function(node) {
27104         
27105         // = settings.validate;
27106         var writer = this.writer;
27107         var self  = this;
27108         this.handlers = {
27109             // #text
27110             3: function(node) {
27111                 
27112                 writer.text(node.nodeValue, node);
27113             },
27114             // #comment
27115             8: function(node) {
27116                 writer.comment(node.nodeValue);
27117             },
27118             // Processing instruction
27119             7: function(node) {
27120                 writer.pi(node.name, node.nodeValue);
27121             },
27122             // Doctype
27123             10: function(node) {
27124                 writer.doctype(node.nodeValue);
27125             },
27126             // CDATA
27127             4: function(node) {
27128                 writer.cdata(node.nodeValue);
27129             },
27130             // Document fragment
27131             11: function(node) {
27132                 node = node.firstChild;
27133                 if (!node) {
27134                     return;
27135                 }
27136                 while(node) {
27137                     self.walk(node);
27138                     node = node.nextSibling
27139                 }
27140             }
27141         };
27142         writer.reset();
27143         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27144         return writer.getContent();
27145     },
27146
27147     walk: function(node)
27148     {
27149         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27150             handler = this.handlers[node.nodeType];
27151             
27152         if (handler) {
27153             handler(node);
27154             return;
27155         }
27156     
27157         var name = node.nodeName;
27158         var isEmpty = node.childNodes.length < 1;
27159       
27160         var writer = this.writer;
27161         var attrs = node.attributes;
27162         // Sort attributes
27163         
27164         writer.start(node.nodeName, attrs, isEmpty, node);
27165         if (isEmpty) {
27166             return;
27167         }
27168         node = node.firstChild;
27169         if (!node) {
27170             writer.end(name);
27171             return;
27172         }
27173         while (node) {
27174             this.walk(node);
27175             node = node.nextSibling;
27176         }
27177         writer.end(name);
27178         
27179     
27180     }
27181     // Serialize element and treat all non elements as fragments
27182    
27183 }; 
27184
27185 /***
27186  * This is based loosely on tinymce 
27187  * @class Roo.htmleditor.TidyWriter
27188  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27189  *
27190  * Known issues?
27191  * - not tested much with 'PRE' formated elements.
27192  * 
27193  *
27194  *
27195  */
27196
27197 Roo.htmleditor.TidyWriter = function(settings)
27198 {
27199     
27200     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27201     Roo.apply(this, settings);
27202     this.html = [];
27203     this.state = [];
27204      
27205     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27206   
27207 }
27208 Roo.htmleditor.TidyWriter.prototype = {
27209
27210  
27211     state : false,
27212     
27213     indent :  '  ',
27214     
27215     // part of state...
27216     indentstr : '',
27217     in_pre: false,
27218     in_inline : false,
27219     last_inline : false,
27220     encode : false,
27221      
27222     
27223             /**
27224     * Writes the a start element such as <p id="a">.
27225     *
27226     * @method start
27227     * @param {String} name Name of the element.
27228     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27229     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27230     */
27231     start: function(name, attrs, empty, node)
27232     {
27233         var i, l, attr, value;
27234         
27235         // there are some situations where adding line break && indentation will not work. will not work.
27236         // <span / b / i ... formating?
27237         
27238         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27239         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27240         
27241         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27242         
27243         var add_lb = name == 'BR' ? false : in_inline;
27244         
27245         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27246             i_inline = false;
27247         }
27248
27249         var indentstr =  this.indentstr;
27250         
27251         // e_inline = elements that can be inline, but still allow \n before and after?
27252         // only 'BR' ??? any others?
27253         
27254         // ADD LINE BEFORE tage
27255         if (!this.in_pre) {
27256             if (in_inline) {
27257                 //code
27258                 if (name == 'BR') {
27259                     this.addLine();
27260                 } else if (this.lastElementEndsWS()) {
27261                     this.addLine();
27262                 } else{
27263                     // otherwise - no new line. (and dont indent.)
27264                     indentstr = '';
27265                 }
27266                 
27267             } else {
27268                 this.addLine();
27269             }
27270         } else {
27271             indentstr = '';
27272         }
27273         
27274         this.html.push(indentstr + '<', name.toLowerCase());
27275         
27276         if (attrs) {
27277             for (i = 0, l = attrs.length; i < l; i++) {
27278                 attr = attrs[i];
27279                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27280             }
27281         }
27282      
27283         if (empty) {
27284             if (is_short) {
27285                 this.html[this.html.length] = '/>';
27286             } else {
27287                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27288             }
27289             var e_inline = name == 'BR' ? false : this.in_inline;
27290             
27291             if (!e_inline && !this.in_pre) {
27292                 this.addLine();
27293             }
27294             return;
27295         
27296         }
27297         // not empty..
27298         this.html[this.html.length] = '>';
27299         
27300         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27301         /*
27302         if (!in_inline && !in_pre) {
27303             var cn = node.firstChild;
27304             while(cn) {
27305                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27306                     in_inline = true
27307                     break;
27308                 }
27309                 cn = cn.nextSibling;
27310             }
27311              
27312         }
27313         */
27314         
27315         
27316         this.pushState({
27317             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27318             in_pre : in_pre,
27319             in_inline :  in_inline
27320         });
27321         // add a line after if we are not in a
27322         
27323         if (!in_inline && !in_pre) {
27324             this.addLine();
27325         }
27326         
27327             
27328          
27329         
27330     },
27331     
27332     lastElementEndsWS : function()
27333     {
27334         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27335         if (value === false) {
27336             return true;
27337         }
27338         return value.match(/\s+$/);
27339         
27340     },
27341     
27342     /**
27343      * Writes the a end element such as </p>.
27344      *
27345      * @method end
27346      * @param {String} name Name of the element.
27347      */
27348     end: function(name) {
27349         var value;
27350         this.popState();
27351         var indentstr = '';
27352         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27353         
27354         if (!this.in_pre && !in_inline) {
27355             this.addLine();
27356             indentstr  = this.indentstr;
27357         }
27358         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27359         this.last_inline = in_inline;
27360         
27361         // pop the indent state..
27362     },
27363     /**
27364      * Writes a text node.
27365      *
27366      * In pre - we should not mess with the contents.
27367      * 
27368      *
27369      * @method text
27370      * @param {String} text String to write out.
27371      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27372      */
27373     text: function(in_text, node)
27374     {
27375         // if not in whitespace critical
27376         if (in_text.length < 1) {
27377             return;
27378         }
27379         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27380         
27381         if (this.in_pre) {
27382             this.html[this.html.length] =  text;
27383             return;   
27384         }
27385         
27386         if (this.in_inline) {
27387             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27388             if (text != ' ') {
27389                 text = text.replace(/\s+/,' ');  // all white space to single white space
27390                 
27391                     
27392                 // if next tag is '<BR>', then we can trim right..
27393                 if (node.nextSibling &&
27394                     node.nextSibling.nodeType == 1 &&
27395                     node.nextSibling.nodeName == 'BR' )
27396                 {
27397                     text = text.replace(/\s+$/g,'');
27398                 }
27399                 // if previous tag was a BR, we can also trim..
27400                 if (node.previousSibling &&
27401                     node.previousSibling.nodeType == 1 &&
27402                     node.previousSibling.nodeName == 'BR' )
27403                 {
27404                     text = this.indentstr +  text.replace(/^\s+/g,'');
27405                 }
27406                 if (text.match(/\n/)) {
27407                     text = text.replace(
27408                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27409                     );
27410                     // remoeve the last whitespace / line break.
27411                     text = text.replace(/\n\s+$/,'');
27412                 }
27413                 // repace long lines
27414                 
27415             }
27416              
27417             this.html[this.html.length] =  text;
27418             return;   
27419         }
27420         // see if previous element was a inline element.
27421         var indentstr = this.indentstr;
27422    
27423         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27424         
27425         // should trim left?
27426         if (node.previousSibling &&
27427             node.previousSibling.nodeType == 1 &&
27428             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27429         {
27430             indentstr = '';
27431             
27432         } else {
27433             this.addLine();
27434             text = text.replace(/^\s+/,''); // trim left
27435           
27436         }
27437         // should trim right?
27438         if (node.nextSibling &&
27439             node.nextSibling.nodeType == 1 &&
27440             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27441         {
27442           // noop
27443             
27444         }  else {
27445             text = text.replace(/\s+$/,''); // trim right
27446         }
27447          
27448               
27449         
27450         
27451         
27452         if (text.length < 1) {
27453             return;
27454         }
27455         if (!text.match(/\n/)) {
27456             this.html.push(indentstr + text);
27457             return;
27458         }
27459         
27460         text = this.indentstr + text.replace(
27461             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27462         );
27463         // remoeve the last whitespace / line break.
27464         text = text.replace(/\s+$/,''); 
27465         
27466         this.html.push(text);
27467         
27468         // split and indent..
27469         
27470         
27471     },
27472     /**
27473      * Writes a cdata node such as <![CDATA[data]]>.
27474      *
27475      * @method cdata
27476      * @param {String} text String to write out inside the cdata.
27477      */
27478     cdata: function(text) {
27479         this.html.push('<![CDATA[', text, ']]>');
27480     },
27481     /**
27482     * Writes a comment node such as <!-- Comment -->.
27483     *
27484     * @method cdata
27485     * @param {String} text String to write out inside the comment.
27486     */
27487    comment: function(text) {
27488        this.html.push('<!--', text, '-->');
27489    },
27490     /**
27491      * Writes a PI node such as <?xml attr="value" ?>.
27492      *
27493      * @method pi
27494      * @param {String} name Name of the pi.
27495      * @param {String} text String to write out inside the pi.
27496      */
27497     pi: function(name, text) {
27498         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27499         this.indent != '' && this.html.push('\n');
27500     },
27501     /**
27502      * Writes a doctype node such as <!DOCTYPE data>.
27503      *
27504      * @method doctype
27505      * @param {String} text String to write out inside the doctype.
27506      */
27507     doctype: function(text) {
27508         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27509     },
27510     /**
27511      * Resets the internal buffer if one wants to reuse the writer.
27512      *
27513      * @method reset
27514      */
27515     reset: function() {
27516         this.html.length = 0;
27517         this.state = [];
27518         this.pushState({
27519             indentstr : '',
27520             in_pre : false, 
27521             in_inline : false
27522         })
27523     },
27524     /**
27525      * Returns the contents that got serialized.
27526      *
27527      * @method getContent
27528      * @return {String} HTML contents that got written down.
27529      */
27530     getContent: function() {
27531         return this.html.join('').replace(/\n$/, '');
27532     },
27533     
27534     pushState : function(cfg)
27535     {
27536         this.state.push(cfg);
27537         Roo.apply(this, cfg);
27538     },
27539     
27540     popState : function()
27541     {
27542         if (this.state.length < 1) {
27543             return; // nothing to push
27544         }
27545         var cfg = {
27546             in_pre: false,
27547             indentstr : ''
27548         };
27549         this.state.pop();
27550         if (this.state.length > 0) {
27551             cfg = this.state[this.state.length-1]; 
27552         }
27553         Roo.apply(this, cfg);
27554     },
27555     
27556     addLine: function()
27557     {
27558         if (this.html.length < 1) {
27559             return;
27560         }
27561         
27562         
27563         var value = this.html[this.html.length - 1];
27564         if (value.length > 0 && '\n' !== value) {
27565             this.html.push('\n');
27566         }
27567     }
27568     
27569     
27570 //'pre script noscript style textarea video audio iframe object code'
27571 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
27572 // inline 
27573 };
27574
27575 Roo.htmleditor.TidyWriter.inline_elements = [
27576         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
27577         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
27578 ];
27579 Roo.htmleditor.TidyWriter.shortend_elements = [
27580     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
27581     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
27582 ];
27583
27584 Roo.htmleditor.TidyWriter.whitespace_elements = [
27585     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
27586 ];/***
27587  * This is based loosely on tinymce 
27588  * @class Roo.htmleditor.TidyEntities
27589  * @static
27590  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27591  *
27592  * Not 100% sure this is actually used or needed.
27593  */
27594
27595 Roo.htmleditor.TidyEntities = {
27596     
27597     /**
27598      * initialize data..
27599      */
27600     init : function (){
27601      
27602         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
27603        
27604     },
27605
27606
27607     buildEntitiesLookup: function(items, radix) {
27608         var i, chr, entity, lookup = {};
27609         if (!items) {
27610             return {};
27611         }
27612         items = typeof(items) == 'string' ? items.split(',') : items;
27613         radix = radix || 10;
27614         // Build entities lookup table
27615         for (i = 0; i < items.length; i += 2) {
27616             chr = String.fromCharCode(parseInt(items[i], radix));
27617             // Only add non base entities
27618             if (!this.baseEntities[chr]) {
27619                 entity = '&' + items[i + 1] + ';';
27620                 lookup[chr] = entity;
27621                 lookup[entity] = chr;
27622             }
27623         }
27624         return lookup;
27625         
27626     },
27627     
27628     asciiMap : {
27629             128: '€',
27630             130: '‚',
27631             131: 'ƒ',
27632             132: '„',
27633             133: '…',
27634             134: '†',
27635             135: '‡',
27636             136: 'ˆ',
27637             137: '‰',
27638             138: 'Š',
27639             139: '‹',
27640             140: 'Œ',
27641             142: 'Ž',
27642             145: '‘',
27643             146: '’',
27644             147: '“',
27645             148: '”',
27646             149: '•',
27647             150: '–',
27648             151: '—',
27649             152: '˜',
27650             153: '™',
27651             154: 'š',
27652             155: '›',
27653             156: 'œ',
27654             158: 'ž',
27655             159: 'Ÿ'
27656     },
27657     // Raw entities
27658     baseEntities : {
27659         '"': '&quot;',
27660         // Needs to be escaped since the YUI compressor would otherwise break the code
27661         '\'': '&#39;',
27662         '<': '&lt;',
27663         '>': '&gt;',
27664         '&': '&amp;',
27665         '`': '&#96;'
27666     },
27667     // Reverse lookup table for raw entities
27668     reverseEntities : {
27669         '&lt;': '<',
27670         '&gt;': '>',
27671         '&amp;': '&',
27672         '&quot;': '"',
27673         '&apos;': '\''
27674     },
27675     
27676     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27677     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27678     rawCharsRegExp : /[<>&\"\']/g,
27679     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
27680     namedEntities  : false,
27681     namedEntitiesData : [ 
27682         '50',
27683         'nbsp',
27684         '51',
27685         'iexcl',
27686         '52',
27687         'cent',
27688         '53',
27689         'pound',
27690         '54',
27691         'curren',
27692         '55',
27693         'yen',
27694         '56',
27695         'brvbar',
27696         '57',
27697         'sect',
27698         '58',
27699         'uml',
27700         '59',
27701         'copy',
27702         '5a',
27703         'ordf',
27704         '5b',
27705         'laquo',
27706         '5c',
27707         'not',
27708         '5d',
27709         'shy',
27710         '5e',
27711         'reg',
27712         '5f',
27713         'macr',
27714         '5g',
27715         'deg',
27716         '5h',
27717         'plusmn',
27718         '5i',
27719         'sup2',
27720         '5j',
27721         'sup3',
27722         '5k',
27723         'acute',
27724         '5l',
27725         'micro',
27726         '5m',
27727         'para',
27728         '5n',
27729         'middot',
27730         '5o',
27731         'cedil',
27732         '5p',
27733         'sup1',
27734         '5q',
27735         'ordm',
27736         '5r',
27737         'raquo',
27738         '5s',
27739         'frac14',
27740         '5t',
27741         'frac12',
27742         '5u',
27743         'frac34',
27744         '5v',
27745         'iquest',
27746         '60',
27747         'Agrave',
27748         '61',
27749         'Aacute',
27750         '62',
27751         'Acirc',
27752         '63',
27753         'Atilde',
27754         '64',
27755         'Auml',
27756         '65',
27757         'Aring',
27758         '66',
27759         'AElig',
27760         '67',
27761         'Ccedil',
27762         '68',
27763         'Egrave',
27764         '69',
27765         'Eacute',
27766         '6a',
27767         'Ecirc',
27768         '6b',
27769         'Euml',
27770         '6c',
27771         'Igrave',
27772         '6d',
27773         'Iacute',
27774         '6e',
27775         'Icirc',
27776         '6f',
27777         'Iuml',
27778         '6g',
27779         'ETH',
27780         '6h',
27781         'Ntilde',
27782         '6i',
27783         'Ograve',
27784         '6j',
27785         'Oacute',
27786         '6k',
27787         'Ocirc',
27788         '6l',
27789         'Otilde',
27790         '6m',
27791         'Ouml',
27792         '6n',
27793         'times',
27794         '6o',
27795         'Oslash',
27796         '6p',
27797         'Ugrave',
27798         '6q',
27799         'Uacute',
27800         '6r',
27801         'Ucirc',
27802         '6s',
27803         'Uuml',
27804         '6t',
27805         'Yacute',
27806         '6u',
27807         'THORN',
27808         '6v',
27809         'szlig',
27810         '70',
27811         'agrave',
27812         '71',
27813         'aacute',
27814         '72',
27815         'acirc',
27816         '73',
27817         'atilde',
27818         '74',
27819         'auml',
27820         '75',
27821         'aring',
27822         '76',
27823         'aelig',
27824         '77',
27825         'ccedil',
27826         '78',
27827         'egrave',
27828         '79',
27829         'eacute',
27830         '7a',
27831         'ecirc',
27832         '7b',
27833         'euml',
27834         '7c',
27835         'igrave',
27836         '7d',
27837         'iacute',
27838         '7e',
27839         'icirc',
27840         '7f',
27841         'iuml',
27842         '7g',
27843         'eth',
27844         '7h',
27845         'ntilde',
27846         '7i',
27847         'ograve',
27848         '7j',
27849         'oacute',
27850         '7k',
27851         'ocirc',
27852         '7l',
27853         'otilde',
27854         '7m',
27855         'ouml',
27856         '7n',
27857         'divide',
27858         '7o',
27859         'oslash',
27860         '7p',
27861         'ugrave',
27862         '7q',
27863         'uacute',
27864         '7r',
27865         'ucirc',
27866         '7s',
27867         'uuml',
27868         '7t',
27869         'yacute',
27870         '7u',
27871         'thorn',
27872         '7v',
27873         'yuml',
27874         'ci',
27875         'fnof',
27876         'sh',
27877         'Alpha',
27878         'si',
27879         'Beta',
27880         'sj',
27881         'Gamma',
27882         'sk',
27883         'Delta',
27884         'sl',
27885         'Epsilon',
27886         'sm',
27887         'Zeta',
27888         'sn',
27889         'Eta',
27890         'so',
27891         'Theta',
27892         'sp',
27893         'Iota',
27894         'sq',
27895         'Kappa',
27896         'sr',
27897         'Lambda',
27898         'ss',
27899         'Mu',
27900         'st',
27901         'Nu',
27902         'su',
27903         'Xi',
27904         'sv',
27905         'Omicron',
27906         't0',
27907         'Pi',
27908         't1',
27909         'Rho',
27910         't3',
27911         'Sigma',
27912         't4',
27913         'Tau',
27914         't5',
27915         'Upsilon',
27916         't6',
27917         'Phi',
27918         't7',
27919         'Chi',
27920         't8',
27921         'Psi',
27922         't9',
27923         'Omega',
27924         'th',
27925         'alpha',
27926         'ti',
27927         'beta',
27928         'tj',
27929         'gamma',
27930         'tk',
27931         'delta',
27932         'tl',
27933         'epsilon',
27934         'tm',
27935         'zeta',
27936         'tn',
27937         'eta',
27938         'to',
27939         'theta',
27940         'tp',
27941         'iota',
27942         'tq',
27943         'kappa',
27944         'tr',
27945         'lambda',
27946         'ts',
27947         'mu',
27948         'tt',
27949         'nu',
27950         'tu',
27951         'xi',
27952         'tv',
27953         'omicron',
27954         'u0',
27955         'pi',
27956         'u1',
27957         'rho',
27958         'u2',
27959         'sigmaf',
27960         'u3',
27961         'sigma',
27962         'u4',
27963         'tau',
27964         'u5',
27965         'upsilon',
27966         'u6',
27967         'phi',
27968         'u7',
27969         'chi',
27970         'u8',
27971         'psi',
27972         'u9',
27973         'omega',
27974         'uh',
27975         'thetasym',
27976         'ui',
27977         'upsih',
27978         'um',
27979         'piv',
27980         '812',
27981         'bull',
27982         '816',
27983         'hellip',
27984         '81i',
27985         'prime',
27986         '81j',
27987         'Prime',
27988         '81u',
27989         'oline',
27990         '824',
27991         'frasl',
27992         '88o',
27993         'weierp',
27994         '88h',
27995         'image',
27996         '88s',
27997         'real',
27998         '892',
27999         'trade',
28000         '89l',
28001         'alefsym',
28002         '8cg',
28003         'larr',
28004         '8ch',
28005         'uarr',
28006         '8ci',
28007         'rarr',
28008         '8cj',
28009         'darr',
28010         '8ck',
28011         'harr',
28012         '8dl',
28013         'crarr',
28014         '8eg',
28015         'lArr',
28016         '8eh',
28017         'uArr',
28018         '8ei',
28019         'rArr',
28020         '8ej',
28021         'dArr',
28022         '8ek',
28023         'hArr',
28024         '8g0',
28025         'forall',
28026         '8g2',
28027         'part',
28028         '8g3',
28029         'exist',
28030         '8g5',
28031         'empty',
28032         '8g7',
28033         'nabla',
28034         '8g8',
28035         'isin',
28036         '8g9',
28037         'notin',
28038         '8gb',
28039         'ni',
28040         '8gf',
28041         'prod',
28042         '8gh',
28043         'sum',
28044         '8gi',
28045         'minus',
28046         '8gn',
28047         'lowast',
28048         '8gq',
28049         'radic',
28050         '8gt',
28051         'prop',
28052         '8gu',
28053         'infin',
28054         '8h0',
28055         'ang',
28056         '8h7',
28057         'and',
28058         '8h8',
28059         'or',
28060         '8h9',
28061         'cap',
28062         '8ha',
28063         'cup',
28064         '8hb',
28065         'int',
28066         '8hk',
28067         'there4',
28068         '8hs',
28069         'sim',
28070         '8i5',
28071         'cong',
28072         '8i8',
28073         'asymp',
28074         '8j0',
28075         'ne',
28076         '8j1',
28077         'equiv',
28078         '8j4',
28079         'le',
28080         '8j5',
28081         'ge',
28082         '8k2',
28083         'sub',
28084         '8k3',
28085         'sup',
28086         '8k4',
28087         'nsub',
28088         '8k6',
28089         'sube',
28090         '8k7',
28091         'supe',
28092         '8kl',
28093         'oplus',
28094         '8kn',
28095         'otimes',
28096         '8l5',
28097         'perp',
28098         '8m5',
28099         'sdot',
28100         '8o8',
28101         'lceil',
28102         '8o9',
28103         'rceil',
28104         '8oa',
28105         'lfloor',
28106         '8ob',
28107         'rfloor',
28108         '8p9',
28109         'lang',
28110         '8pa',
28111         'rang',
28112         '9ea',
28113         'loz',
28114         '9j0',
28115         'spades',
28116         '9j3',
28117         'clubs',
28118         '9j5',
28119         'hearts',
28120         '9j6',
28121         'diams',
28122         'ai',
28123         'OElig',
28124         'aj',
28125         'oelig',
28126         'b0',
28127         'Scaron',
28128         'b1',
28129         'scaron',
28130         'bo',
28131         'Yuml',
28132         'm6',
28133         'circ',
28134         'ms',
28135         'tilde',
28136         '802',
28137         'ensp',
28138         '803',
28139         'emsp',
28140         '809',
28141         'thinsp',
28142         '80c',
28143         'zwnj',
28144         '80d',
28145         'zwj',
28146         '80e',
28147         'lrm',
28148         '80f',
28149         'rlm',
28150         '80j',
28151         'ndash',
28152         '80k',
28153         'mdash',
28154         '80o',
28155         'lsquo',
28156         '80p',
28157         'rsquo',
28158         '80q',
28159         'sbquo',
28160         '80s',
28161         'ldquo',
28162         '80t',
28163         'rdquo',
28164         '80u',
28165         'bdquo',
28166         '810',
28167         'dagger',
28168         '811',
28169         'Dagger',
28170         '81g',
28171         'permil',
28172         '81p',
28173         'lsaquo',
28174         '81q',
28175         'rsaquo',
28176         '85c',
28177         'euro'
28178     ],
28179
28180          
28181     /**
28182      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28183      *
28184      * @method encodeRaw
28185      * @param {String} text Text to encode.
28186      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28187      * @return {String} Entity encoded text.
28188      */
28189     encodeRaw: function(text, attr)
28190     {
28191         var t = this;
28192         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28193             return t.baseEntities[chr] || chr;
28194         });
28195     },
28196     /**
28197      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28198      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28199      * and is exposed as the DOMUtils.encode function.
28200      *
28201      * @method encodeAllRaw
28202      * @param {String} text Text to encode.
28203      * @return {String} Entity encoded text.
28204      */
28205     encodeAllRaw: function(text) {
28206         var t = this;
28207         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28208             return t.baseEntities[chr] || chr;
28209         });
28210     },
28211     /**
28212      * Encodes the specified string using numeric entities. The core entities will be
28213      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28214      *
28215      * @method encodeNumeric
28216      * @param {String} text Text to encode.
28217      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28218      * @return {String} Entity encoded text.
28219      */
28220     encodeNumeric: function(text, attr) {
28221         var t = this;
28222         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28223             // Multi byte sequence convert it to a single entity
28224             if (chr.length > 1) {
28225                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28226             }
28227             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28228         });
28229     },
28230     /**
28231      * Encodes the specified string using named entities. The core entities will be encoded
28232      * as named ones but all non lower ascii characters will be encoded into named entities.
28233      *
28234      * @method encodeNamed
28235      * @param {String} text Text to encode.
28236      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28237      * @param {Object} entities Optional parameter with entities to use.
28238      * @return {String} Entity encoded text.
28239      */
28240     encodeNamed: function(text, attr, entities) {
28241         var t = this;
28242         entities = entities || this.namedEntities;
28243         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28244             return t.baseEntities[chr] || entities[chr] || chr;
28245         });
28246     },
28247     /**
28248      * Returns an encode function based on the name(s) and it's optional entities.
28249      *
28250      * @method getEncodeFunc
28251      * @param {String} name Comma separated list of encoders for example named,numeric.
28252      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28253      * @return {function} Encode function to be used.
28254      */
28255     getEncodeFunc: function(name, entities) {
28256         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28257         var t = this;
28258         function encodeNamedAndNumeric(text, attr) {
28259             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28260                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28261             });
28262         }
28263
28264         function encodeCustomNamed(text, attr) {
28265             return t.encodeNamed(text, attr, entities);
28266         }
28267         // Replace + with , to be compatible with previous TinyMCE versions
28268         name = this.makeMap(name.replace(/\+/g, ','));
28269         // Named and numeric encoder
28270         if (name.named && name.numeric) {
28271             return this.encodeNamedAndNumeric;
28272         }
28273         // Named encoder
28274         if (name.named) {
28275             // Custom names
28276             if (entities) {
28277                 return encodeCustomNamed;
28278             }
28279             return this.encodeNamed;
28280         }
28281         // Numeric
28282         if (name.numeric) {
28283             return this.encodeNumeric;
28284         }
28285         // Raw encoder
28286         return this.encodeRaw;
28287     },
28288     /**
28289      * Decodes the specified string, this will replace entities with raw UTF characters.
28290      *
28291      * @method decode
28292      * @param {String} text Text to entity decode.
28293      * @return {String} Entity decoded string.
28294      */
28295     decode: function(text)
28296     {
28297         var  t = this;
28298         return text.replace(this.entityRegExp, function(all, numeric) {
28299             if (numeric) {
28300                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28301                 // Support upper UTF
28302                 if (numeric > 65535) {
28303                     numeric -= 65536;
28304                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28305                 }
28306                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28307             }
28308             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28309         });
28310     },
28311     nativeDecode : function (text) {
28312         return text;
28313     },
28314     makeMap : function (items, delim, map) {
28315                 var i;
28316                 items = items || [];
28317                 delim = delim || ',';
28318                 if (typeof items == "string") {
28319                         items = items.split(delim);
28320                 }
28321                 map = map || {};
28322                 i = items.length;
28323                 while (i--) {
28324                         map[items[i]] = {};
28325                 }
28326                 return map;
28327         }
28328 };
28329     
28330     
28331     
28332 Roo.htmleditor.TidyEntities.init();
28333 /**
28334  * @class Roo.htmleditor.KeyEnter
28335  * Handle Enter press..
28336  * @cfg {Roo.HtmlEditorCore} core the editor.
28337  * @constructor
28338  * Create a new Filter.
28339  * @param {Object} config Configuration options
28340  */
28341
28342
28343
28344
28345
28346 Roo.htmleditor.KeyEnter = function(cfg) {
28347     Roo.apply(this, cfg);
28348     // this does not actually call walk as it's really just a abstract class
28349  
28350     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28351 }
28352
28353 //Roo.htmleditor.KeyEnter.i = 0;
28354
28355
28356 Roo.htmleditor.KeyEnter.prototype = {
28357     
28358     core : false,
28359     
28360     keypress : function(e)
28361     {
28362         if (e.charCode != 13 && e.charCode != 10) {
28363             Roo.log([e.charCode,e]);
28364             return true;
28365         }
28366         e.preventDefault();
28367         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28368         var doc = this.core.doc;
28369           //add a new line
28370        
28371     
28372         var sel = this.core.getSelection();
28373         var range = sel.getRangeAt(0);
28374         var n = range.commonAncestorContainer;
28375         var pc = range.closest([ 'ol', 'ul']);
28376         var pli = range.closest('li');
28377         if (!pc || e.ctrlKey) {
28378             // on it list, or ctrl pressed.
28379             if (!e.ctrlKey) {
28380                 sel.insertNode('br', 'after'); 
28381             } else {
28382                 // only do this if we have ctrl key..
28383                 var br = doc.createElement('br');
28384                 br.className = 'clear';
28385                 br.setAttribute('style', 'clear: both');
28386                 sel.insertNode(br, 'after'); 
28387             }
28388             
28389          
28390             this.core.undoManager.addEvent();
28391             this.core.fireEditorEvent(e);
28392             return false;
28393         }
28394         
28395         // deal with <li> insetion
28396         if (pli.innerText.trim() == '' &&
28397             pli.previousSibling &&
28398             pli.previousSibling.nodeName == 'LI' &&
28399             pli.previousSibling.innerText.trim() ==  '') {
28400             pli.parentNode.removeChild(pli.previousSibling);
28401             sel.cursorAfter(pc);
28402             this.core.undoManager.addEvent();
28403             this.core.fireEditorEvent(e);
28404             return false;
28405         }
28406     
28407         var li = doc.createElement('LI');
28408         li.innerHTML = '&nbsp;';
28409         if (!pli || !pli.firstSibling) {
28410             pc.appendChild(li);
28411         } else {
28412             pli.parentNode.insertBefore(li, pli.firstSibling);
28413         }
28414         sel.cursorText (li.firstChild);
28415       
28416         this.core.undoManager.addEvent();
28417         this.core.fireEditorEvent(e);
28418
28419         return false;
28420         
28421     
28422         
28423         
28424          
28425     }
28426 };
28427      
28428 /**
28429  * @class Roo.htmleditor.Block
28430  * Base class for html editor blocks - do not use it directly .. extend it..
28431  * @cfg {DomElement} node The node to apply stuff to.
28432  * @cfg {String} friendly_name the name that appears in the context bar about this block
28433  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28434  
28435  * @constructor
28436  * Create a new Filter.
28437  * @param {Object} config Configuration options
28438  */
28439
28440 Roo.htmleditor.Block  = function(cfg)
28441 {
28442     // do nothing .. should not be called really.
28443 }
28444 /**
28445  * factory method to get the block from an element (using cache if necessary)
28446  * @static
28447  * @param {HtmlElement} the dom element
28448  */
28449 Roo.htmleditor.Block.factory = function(node)
28450 {
28451     var cc = Roo.htmleditor.Block.cache;
28452     var id = Roo.get(node).id;
28453     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28454         Roo.htmleditor.Block.cache[id].readElement(node);
28455         return Roo.htmleditor.Block.cache[id];
28456     }
28457     var db  = node.getAttribute('data-block');
28458     if (!db) {
28459         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28460     }
28461     var cls = Roo.htmleditor['Block' + db];
28462     if (typeof(cls) == 'undefined') {
28463         //Roo.log(node.getAttribute('data-block'));
28464         Roo.log("OOps missing block : " + 'Block' + db);
28465         return false;
28466     }
28467     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28468     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
28469 };
28470
28471 /**
28472  * initalize all Elements from content that are 'blockable'
28473  * @static
28474  * @param the body element
28475  */
28476 Roo.htmleditor.Block.initAll = function(body, type)
28477 {
28478     if (typeof(type) == 'undefined') {
28479         var ia = Roo.htmleditor.Block.initAll;
28480         ia(body,'table');
28481         ia(body,'td');
28482         ia(body,'figure');
28483         return;
28484     }
28485     Roo.each(Roo.get(body).query(type), function(e) {
28486         Roo.htmleditor.Block.factory(e);    
28487     },this);
28488 };
28489 // question goes here... do we need to clear out this cache sometimes?
28490 // or show we make it relivant to the htmleditor.
28491 Roo.htmleditor.Block.cache = {};
28492
28493 Roo.htmleditor.Block.prototype = {
28494     
28495     node : false,
28496     
28497      // used by context menu
28498     friendly_name : 'Based Block',
28499     
28500     // text for button to delete this element
28501     deleteTitle : false,
28502     
28503     context : false,
28504     /**
28505      * Update a node with values from this object
28506      * @param {DomElement} node
28507      */
28508     updateElement : function(node)
28509     {
28510         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28511     },
28512      /**
28513      * convert to plain HTML for calling insertAtCursor..
28514      */
28515     toHTML : function()
28516     {
28517         return Roo.DomHelper.markup(this.toObject());
28518     },
28519     /**
28520      * used by readEleemnt to extract data from a node
28521      * may need improving as it's pretty basic
28522      
28523      * @param {DomElement} node
28524      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28525      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28526      * @param {String} style the style property - eg. text-align
28527      */
28528     getVal : function(node, tag, attr, style)
28529     {
28530         var n = node;
28531         if (tag !== true && n.tagName != tag.toUpperCase()) {
28532             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28533             // but kiss for now.
28534             n = node.getElementsByTagName(tag).item(0);
28535         }
28536         if (!n) {
28537             return '';
28538         }
28539         if (attr === false) {
28540             return n;
28541         }
28542         if (attr == 'html') {
28543             return n.innerHTML;
28544         }
28545         if (attr == 'style') {
28546             return n.style[style]; 
28547         }
28548         
28549         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
28550             
28551     },
28552     /**
28553      * create a DomHelper friendly object - for use with 
28554      * Roo.DomHelper.markup / overwrite / etc..
28555      * (override this)
28556      */
28557     toObject : function()
28558     {
28559         return {};
28560     },
28561       /**
28562      * Read a node that has a 'data-block' property - and extract the values from it.
28563      * @param {DomElement} node - the node
28564      */
28565     readElement : function(node)
28566     {
28567         
28568     } 
28569     
28570     
28571 };
28572
28573  
28574
28575 /**
28576  * @class Roo.htmleditor.BlockFigure
28577  * Block that has an image and a figcaption
28578  * @cfg {String} image_src the url for the image
28579  * @cfg {String} align (left|right) alignment for the block default left
28580  * @cfg {String} caption the text to appear below  (and in the alt tag)
28581  * @cfg {String} caption_display (block|none) display or not the caption
28582  * @cfg {String|number} image_width the width of the image number or %?
28583  * @cfg {String|number} image_height the height of the image number or %?
28584  * 
28585  * @constructor
28586  * Create a new Filter.
28587  * @param {Object} config Configuration options
28588  */
28589
28590 Roo.htmleditor.BlockFigure = function(cfg)
28591 {
28592     if (cfg.node) {
28593         this.readElement(cfg.node);
28594         this.updateElement(cfg.node);
28595     }
28596     Roo.apply(this, cfg);
28597 }
28598 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
28599  
28600     
28601     // setable values.
28602     image_src: '',
28603     align: 'center',
28604     caption : '',
28605     caption_display : 'block',
28606     width : '100%',
28607     cls : '',
28608     href: '',
28609     video_url : '',
28610     
28611     // margin: '2%', not used
28612     
28613     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
28614
28615     
28616     // used by context menu
28617     friendly_name : 'Image with caption',
28618     deleteTitle : "Delete Image and Caption",
28619     
28620     contextMenu : function(toolbar)
28621     {
28622         
28623         var block = function() {
28624             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28625         };
28626         
28627         
28628         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28629         
28630         var syncValue = toolbar.editorcore.syncValue;
28631         
28632         var fields = {};
28633         
28634         return [
28635              {
28636                 xtype : 'TextItem',
28637                 text : "Source: ",
28638                 xns : rooui.Toolbar  //Boostrap?
28639             },
28640             {
28641                 xtype : 'Button',
28642                 text: 'Change Image URL',
28643                  
28644                 listeners : {
28645                     click: function (btn, state)
28646                     {
28647                         var b = block();
28648                         
28649                         Roo.MessageBox.show({
28650                             title : "Image Source URL",
28651                             msg : "Enter the url for the image",
28652                             buttons: Roo.MessageBox.OKCANCEL,
28653                             fn: function(btn, val){
28654                                 if (btn != 'ok') {
28655                                     return;
28656                                 }
28657                                 b.image_src = val;
28658                                 b.updateElement();
28659                                 syncValue();
28660                                 toolbar.editorcore.onEditorEvent();
28661                             },
28662                             minWidth:250,
28663                             prompt:true,
28664                             //multiline: multiline,
28665                             modal : true,
28666                             value : b.image_src
28667                         });
28668                     }
28669                 },
28670                 xns : rooui.Toolbar
28671             },
28672          
28673             {
28674                 xtype : 'Button',
28675                 text: 'Change Link URL',
28676                  
28677                 listeners : {
28678                     click: function (btn, state)
28679                     {
28680                         var b = block();
28681                         
28682                         Roo.MessageBox.show({
28683                             title : "Link URL",
28684                             msg : "Enter the url for the link - leave blank to have no link",
28685                             buttons: Roo.MessageBox.OKCANCEL,
28686                             fn: function(btn, val){
28687                                 if (btn != 'ok') {
28688                                     return;
28689                                 }
28690                                 b.href = val;
28691                                 b.updateElement();
28692                                 syncValue();
28693                                 toolbar.editorcore.onEditorEvent();
28694                             },
28695                             minWidth:250,
28696                             prompt:true,
28697                             //multiline: multiline,
28698                             modal : true,
28699                             value : b.href
28700                         });
28701                     }
28702                 },
28703                 xns : rooui.Toolbar
28704             },
28705             {
28706                 xtype : 'Button',
28707                 text: 'Show Video URL',
28708                  
28709                 listeners : {
28710                     click: function (btn, state)
28711                     {
28712                         Roo.MessageBox.alert("Video URL",
28713                             block().video_url == '' ? 'This image is not linked ot a video' :
28714                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
28715                     }
28716                 },
28717                 xns : rooui.Toolbar
28718             },
28719             
28720             
28721             {
28722                 xtype : 'TextItem',
28723                 text : "Width: ",
28724                 xns : rooui.Toolbar  //Boostrap?
28725             },
28726             {
28727                 xtype : 'ComboBox',
28728                 allowBlank : false,
28729                 displayField : 'val',
28730                 editable : true,
28731                 listWidth : 100,
28732                 triggerAction : 'all',
28733                 typeAhead : true,
28734                 valueField : 'val',
28735                 width : 70,
28736                 name : 'width',
28737                 listeners : {
28738                     select : function (combo, r, index)
28739                     {
28740                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28741                         var b = block();
28742                         b.width = r.get('val');
28743                         b.updateElement();
28744                         syncValue();
28745                         toolbar.editorcore.onEditorEvent();
28746                     }
28747                 },
28748                 xns : rooui.form,
28749                 store : {
28750                     xtype : 'SimpleStore',
28751                     data : [
28752                         ['100%'],
28753                         ['80%'],
28754                         ['50%'],
28755                         ['20%'],
28756                         ['10%']
28757                     ],
28758                     fields : [ 'val'],
28759                     xns : Roo.data
28760                 }
28761             },
28762             {
28763                 xtype : 'TextItem',
28764                 text : "Align: ",
28765                 xns : rooui.Toolbar  //Boostrap?
28766             },
28767             {
28768                 xtype : 'ComboBox',
28769                 allowBlank : false,
28770                 displayField : 'val',
28771                 editable : true,
28772                 listWidth : 100,
28773                 triggerAction : 'all',
28774                 typeAhead : true,
28775                 valueField : 'val',
28776                 width : 70,
28777                 name : 'align',
28778                 listeners : {
28779                     select : function (combo, r, index)
28780                     {
28781                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28782                         var b = block();
28783                         b.align = r.get('val');
28784                         b.updateElement();
28785                         syncValue();
28786                         toolbar.editorcore.onEditorEvent();
28787                     }
28788                 },
28789                 xns : rooui.form,
28790                 store : {
28791                     xtype : 'SimpleStore',
28792                     data : [
28793                         ['left'],
28794                         ['right'],
28795                         ['center']
28796                     ],
28797                     fields : [ 'val'],
28798                     xns : Roo.data
28799                 }
28800             },
28801             
28802             
28803             {
28804                 xtype : 'Button',
28805                 text: 'Hide Caption',
28806                 name : 'caption_display',
28807                 pressed : false,
28808                 enableToggle : true,
28809                 setValue : function(v) {
28810                     // this trigger toggle.
28811                      
28812                     this.setText(v ? "Hide Caption" : "Show Caption");
28813                     this.setPressed(v != 'block');
28814                 },
28815                 listeners : {
28816                     toggle: function (btn, state)
28817                     {
28818                         var b  = block();
28819                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
28820                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
28821                         b.updateElement();
28822                         syncValue();
28823                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28824                         toolbar.editorcore.onEditorEvent();
28825                     }
28826                 },
28827                 xns : rooui.Toolbar
28828             }
28829         ];
28830         
28831     },
28832     /**
28833      * create a DomHelper friendly object - for use with
28834      * Roo.DomHelper.markup / overwrite / etc..
28835      */
28836     toObject : function()
28837     {
28838         var d = document.createElement('div');
28839         d.innerHTML = this.caption;
28840         
28841         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
28842         
28843         var iw = this.align == 'center' ? this.width : '100%';
28844         var img =   {
28845             tag : 'img',
28846             contenteditable : 'false',
28847             src : this.image_src,
28848             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
28849             style: {
28850                 width : iw,
28851                 maxWidth : iw + ' !important', // this is not getting rendered?
28852                 margin : m  
28853                 
28854             }
28855         };
28856         /*
28857         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
28858                     '<a href="{2}">' + 
28859                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
28860                     '</a>' + 
28861                 '</div>',
28862         */
28863                 
28864         if (this.href.length > 0) {
28865             img = {
28866                 tag : 'a',
28867                 href: this.href,
28868                 contenteditable : 'true',
28869                 cn : [
28870                     img
28871                 ]
28872             };
28873         }
28874         
28875         
28876         if (this.video_url.length > 0) {
28877             img = {
28878                 tag : 'div',
28879                 cls : this.cls,
28880                 frameborder : 0,
28881                 allowfullscreen : true,
28882                 width : 420,  // these are for video tricks - that we replace the outer
28883                 height : 315,
28884                 src : this.video_url,
28885                 cn : [
28886                     img
28887                 ]
28888             };
28889         }
28890         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
28891         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
28892         
28893   
28894         var ret =   {
28895             tag: 'figure',
28896             'data-block' : 'Figure',
28897             'data-width' : this.width, 
28898             contenteditable : 'false',
28899             
28900             style : {
28901                 display: 'block',
28902                 float :  this.align ,
28903                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
28904                 width : this.align == 'center' ? '100%' : this.width,
28905                 margin:  '0px',
28906                 padding: this.align == 'center' ? '0' : '0 10px' ,
28907                 textAlign : this.align   // seems to work for email..
28908                 
28909             },
28910            
28911             
28912             align : this.align,
28913             cn : [
28914                 img,
28915               
28916                 {
28917                     tag: 'figcaption',
28918                     'data-display' : this.caption_display,
28919                     style : {
28920                         textAlign : 'left',
28921                         fontSize : '16px',
28922                         lineHeight : '24px',
28923                         display : this.caption_display,
28924                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
28925                         margin: m,
28926                         width: this.align == 'center' ?  this.width : '100%' 
28927                     
28928                          
28929                     },
28930                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
28931                     cn : [
28932                         {
28933                             tag: 'div',
28934                             style  : {
28935                                 marginTop : '16px',
28936                                 textAlign : 'left'
28937                             },
28938                             align: 'left',
28939                             cn : [
28940                                 {
28941                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
28942                                     tag : 'i',
28943                                     contenteditable : true,
28944                                     html : captionhtml
28945                                 }
28946                                 
28947                             ]
28948                         }
28949                         
28950                     ]
28951                     
28952                 }
28953             ]
28954         };
28955         return ret;
28956          
28957     },
28958     
28959     readElement : function(node)
28960     {
28961         // this should not really come from the link...
28962         this.video_url = this.getVal(node, 'div', 'src');
28963         this.cls = this.getVal(node, 'div', 'class');
28964         this.href = this.getVal(node, 'a', 'href');
28965         
28966         
28967         this.image_src = this.getVal(node, 'img', 'src');
28968          
28969         this.align = this.getVal(node, 'figure', 'align');
28970         var figcaption = this.getVal(node, 'figcaption', false);
28971         if (figcaption !== '') {
28972             this.caption = this.getVal(figcaption, 'i', 'html');
28973         }
28974         
28975
28976         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
28977         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
28978         this.width = this.getVal(node, true, 'data-width');
28979         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
28980         
28981     },
28982     removeNode : function()
28983     {
28984         return this.node;
28985     }
28986     
28987   
28988    
28989      
28990     
28991     
28992     
28993     
28994 })
28995
28996  
28997
28998 /**
28999  * @class Roo.htmleditor.BlockTable
29000  * Block that manages a table
29001  * 
29002  * @constructor
29003  * Create a new Filter.
29004  * @param {Object} config Configuration options
29005  */
29006
29007 Roo.htmleditor.BlockTable = function(cfg)
29008 {
29009     if (cfg.node) {
29010         this.readElement(cfg.node);
29011         this.updateElement(cfg.node);
29012     }
29013     Roo.apply(this, cfg);
29014     if (!cfg.node) {
29015         this.rows = [];
29016         for(var r = 0; r < this.no_row; r++) {
29017             this.rows[r] = [];
29018             for(var c = 0; c < this.no_col; c++) {
29019                 this.rows[r][c] = this.emptyCell();
29020             }
29021         }
29022     }
29023     
29024     
29025 }
29026 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29027  
29028     rows : false,
29029     no_col : 1,
29030     no_row : 1,
29031     
29032     
29033     width: '100%',
29034     
29035     // used by context menu
29036     friendly_name : 'Table',
29037     deleteTitle : 'Delete Table',
29038     // context menu is drawn once..
29039     
29040     contextMenu : function(toolbar)
29041     {
29042         
29043         var block = function() {
29044             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29045         };
29046         
29047         
29048         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29049         
29050         var syncValue = toolbar.editorcore.syncValue;
29051         
29052         var fields = {};
29053         
29054         return [
29055             {
29056                 xtype : 'TextItem',
29057                 text : "Width: ",
29058                 xns : rooui.Toolbar  //Boostrap?
29059             },
29060             {
29061                 xtype : 'ComboBox',
29062                 allowBlank : false,
29063                 displayField : 'val',
29064                 editable : true,
29065                 listWidth : 100,
29066                 triggerAction : 'all',
29067                 typeAhead : true,
29068                 valueField : 'val',
29069                 width : 100,
29070                 name : 'width',
29071                 listeners : {
29072                     select : function (combo, r, index)
29073                     {
29074                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29075                         var b = block();
29076                         b.width = r.get('val');
29077                         b.updateElement();
29078                         syncValue();
29079                         toolbar.editorcore.onEditorEvent();
29080                     }
29081                 },
29082                 xns : rooui.form,
29083                 store : {
29084                     xtype : 'SimpleStore',
29085                     data : [
29086                         ['100%'],
29087                         ['auto']
29088                     ],
29089                     fields : [ 'val'],
29090                     xns : Roo.data
29091                 }
29092             },
29093             // -------- Cols
29094             
29095             {
29096                 xtype : 'TextItem',
29097                 text : "Columns: ",
29098                 xns : rooui.Toolbar  //Boostrap?
29099             },
29100          
29101             {
29102                 xtype : 'Button',
29103                 text: '-',
29104                 listeners : {
29105                     click : function (_self, e)
29106                     {
29107                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29108                         block().removeColumn();
29109                         syncValue();
29110                         toolbar.editorcore.onEditorEvent();
29111                     }
29112                 },
29113                 xns : rooui.Toolbar
29114             },
29115             {
29116                 xtype : 'Button',
29117                 text: '+',
29118                 listeners : {
29119                     click : function (_self, e)
29120                     {
29121                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29122                         block().addColumn();
29123                         syncValue();
29124                         toolbar.editorcore.onEditorEvent();
29125                     }
29126                 },
29127                 xns : rooui.Toolbar
29128             },
29129             // -------- ROWS
29130             {
29131                 xtype : 'TextItem',
29132                 text : "Rows: ",
29133                 xns : rooui.Toolbar  //Boostrap?
29134             },
29135          
29136             {
29137                 xtype : 'Button',
29138                 text: '-',
29139                 listeners : {
29140                     click : function (_self, e)
29141                     {
29142                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29143                         block().removeRow();
29144                         syncValue();
29145                         toolbar.editorcore.onEditorEvent();
29146                     }
29147                 },
29148                 xns : rooui.Toolbar
29149             },
29150             {
29151                 xtype : 'Button',
29152                 text: '+',
29153                 listeners : {
29154                     click : function (_self, e)
29155                     {
29156                         block().addRow();
29157                         syncValue();
29158                         toolbar.editorcore.onEditorEvent();
29159                     }
29160                 },
29161                 xns : rooui.Toolbar
29162             },
29163             // -------- ROWS
29164             {
29165                 xtype : 'Button',
29166                 text: 'Reset Column Widths',
29167                 listeners : {
29168                     
29169                     click : function (_self, e)
29170                     {
29171                         block().resetWidths();
29172                         syncValue();
29173                         toolbar.editorcore.onEditorEvent();
29174                     }
29175                 },
29176                 xns : rooui.Toolbar
29177             } 
29178             
29179             
29180             
29181         ];
29182         
29183     },
29184     
29185     
29186   /**
29187      * create a DomHelper friendly object - for use with
29188      * Roo.DomHelper.markup / overwrite / etc..
29189      * ?? should it be called with option to hide all editing features?
29190      */
29191     toObject : function()
29192     {
29193         
29194         var ret = {
29195             tag : 'table',
29196             contenteditable : 'false', // this stops cell selection from picking the table.
29197             'data-block' : 'Table',
29198             style : {
29199                 width:  this.width,
29200                 border : 'solid 1px #000', // ??? hard coded?
29201                 'border-collapse' : 'collapse' 
29202             },
29203             cn : [
29204                 { tag : 'tbody' , cn : [] }
29205             ]
29206         };
29207         
29208         // do we have a head = not really 
29209         var ncols = 0;
29210         Roo.each(this.rows, function( row ) {
29211             var tr = {
29212                 tag: 'tr',
29213                 style : {
29214                     margin: '6px',
29215                     border : 'solid 1px #000',
29216                     textAlign : 'left' 
29217                 },
29218                 cn : [ ]
29219             };
29220             
29221             ret.cn[0].cn.push(tr);
29222             // does the row have any properties? ?? height?
29223             var nc = 0;
29224             Roo.each(row, function( cell ) {
29225                 
29226                 var td = {
29227                     tag : 'td',
29228                     contenteditable :  'true',
29229                     'data-block' : 'Td',
29230                     html : cell.html,
29231                     style : cell.style
29232                 };
29233                 if (cell.colspan > 1) {
29234                     td.colspan = cell.colspan ;
29235                     nc += cell.colspan;
29236                 } else {
29237                     nc++;
29238                 }
29239                 if (cell.rowspan > 1) {
29240                     td.rowspan = cell.rowspan ;
29241                 }
29242                 
29243                 
29244                 // widths ?
29245                 tr.cn.push(td);
29246                     
29247                 
29248             }, this);
29249             ncols = Math.max(nc, ncols);
29250             
29251             
29252         }, this);
29253         // add the header row..
29254         
29255         ncols++;
29256          
29257         
29258         return ret;
29259          
29260     },
29261     
29262     readElement : function(node)
29263     {
29264         node  = node ? node : this.node ;
29265         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29266         
29267         this.rows = [];
29268         this.no_row = 0;
29269         var trs = Array.from(node.rows);
29270         trs.forEach(function(tr) {
29271             var row =  [];
29272             this.rows.push(row);
29273             
29274             this.no_row++;
29275             var no_column = 0;
29276             Array.from(tr.cells).forEach(function(td) {
29277                 
29278                 var add = {
29279                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29280                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29281                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29282                     html : td.innerHTML
29283                 };
29284                 no_column += add.colspan;
29285                      
29286                 
29287                 row.push(add);
29288                 
29289                 
29290             },this);
29291             this.no_col = Math.max(this.no_col, no_column);
29292             
29293             
29294         },this);
29295         
29296         
29297     },
29298     normalizeRows: function()
29299     {
29300         var ret= [];
29301         var rid = -1;
29302         this.rows.forEach(function(row) {
29303             rid++;
29304             ret[rid] = [];
29305             row = this.normalizeRow(row);
29306             var cid = 0;
29307             row.forEach(function(c) {
29308                 while (typeof(ret[rid][cid]) != 'undefined') {
29309                     cid++;
29310                 }
29311                 if (typeof(ret[rid]) == 'undefined') {
29312                     ret[rid] = [];
29313                 }
29314                 ret[rid][cid] = c;
29315                 c.row = rid;
29316                 c.col = cid;
29317                 if (c.rowspan < 2) {
29318                     return;
29319                 }
29320                 
29321                 for(var i = 1 ;i < c.rowspan; i++) {
29322                     if (typeof(ret[rid+i]) == 'undefined') {
29323                         ret[rid+i] = [];
29324                     }
29325                     ret[rid+i][cid] = c;
29326                 }
29327             });
29328         }, this);
29329         return ret;
29330     
29331     },
29332     
29333     normalizeRow: function(row)
29334     {
29335         var ret= [];
29336         row.forEach(function(c) {
29337             if (c.colspan < 2) {
29338                 ret.push(c);
29339                 return;
29340             }
29341             for(var i =0 ;i < c.colspan; i++) {
29342                 ret.push(c);
29343             }
29344         });
29345         return ret;
29346     
29347     },
29348     
29349     deleteColumn : function(sel)
29350     {
29351         if (!sel || sel.type != 'col') {
29352             return;
29353         }
29354         if (this.no_col < 2) {
29355             return;
29356         }
29357         
29358         this.rows.forEach(function(row) {
29359             var cols = this.normalizeRow(row);
29360             var col = cols[sel.col];
29361             if (col.colspan > 1) {
29362                 col.colspan --;
29363             } else {
29364                 row.remove(col);
29365             }
29366             
29367         }, this);
29368         this.no_col--;
29369         
29370     },
29371     removeColumn : function()
29372     {
29373         this.deleteColumn({
29374             type: 'col',
29375             col : this.no_col-1
29376         });
29377         this.updateElement();
29378     },
29379     
29380      
29381     addColumn : function()
29382     {
29383         
29384         this.rows.forEach(function(row) {
29385             row.push(this.emptyCell());
29386            
29387         }, this);
29388         this.updateElement();
29389     },
29390     
29391     deleteRow : function(sel)
29392     {
29393         if (!sel || sel.type != 'row') {
29394             return;
29395         }
29396         
29397         if (this.no_row < 2) {
29398             return;
29399         }
29400         
29401         var rows = this.normalizeRows();
29402         
29403         
29404         rows[sel.row].forEach(function(col) {
29405             if (col.rowspan > 1) {
29406                 col.rowspan--;
29407             } else {
29408                 col.remove = 1; // flage it as removed.
29409             }
29410             
29411         }, this);
29412         var newrows = [];
29413         this.rows.forEach(function(row) {
29414             newrow = [];
29415             row.forEach(function(c) {
29416                 if (typeof(c.remove) == 'undefined') {
29417                     newrow.push(c);
29418                 }
29419                 
29420             });
29421             if (newrow.length > 0) {
29422                 newrows.push(row);
29423             }
29424         });
29425         this.rows =  newrows;
29426         
29427         
29428         
29429         this.no_row--;
29430         this.updateElement();
29431         
29432     },
29433     removeRow : function()
29434     {
29435         this.deleteRow({
29436             type: 'row',
29437             row : this.no_row-1
29438         });
29439         
29440     },
29441     
29442      
29443     addRow : function()
29444     {
29445         
29446         var row = [];
29447         for (var i = 0; i < this.no_col; i++ ) {
29448             
29449             row.push(this.emptyCell());
29450            
29451         }
29452         this.rows.push(row);
29453         this.updateElement();
29454         
29455     },
29456      
29457     // the default cell object... at present...
29458     emptyCell : function() {
29459         return (new Roo.htmleditor.BlockTd({})).toObject();
29460         
29461      
29462     },
29463     
29464     removeNode : function()
29465     {
29466         return this.node;
29467     },
29468     
29469     
29470     
29471     resetWidths : function()
29472     {
29473         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29474             var nn = Roo.htmleditor.Block.factory(n);
29475             nn.width = '';
29476             nn.updateElement(n);
29477         });
29478     }
29479     
29480     
29481     
29482     
29483 })
29484
29485 /**
29486  *
29487  * editing a TD?
29488  *
29489  * since selections really work on the table cell, then editing really should work from there
29490  *
29491  * The original plan was to support merging etc... - but that may not be needed yet..
29492  *
29493  * So this simple version will support:
29494  *   add/remove cols
29495  *   adjust the width +/-
29496  *   reset the width...
29497  *   
29498  *
29499  */
29500
29501
29502  
29503
29504 /**
29505  * @class Roo.htmleditor.BlockTable
29506  * Block that manages a table
29507  * 
29508  * @constructor
29509  * Create a new Filter.
29510  * @param {Object} config Configuration options
29511  */
29512
29513 Roo.htmleditor.BlockTd = function(cfg)
29514 {
29515     if (cfg.node) {
29516         this.readElement(cfg.node);
29517         this.updateElement(cfg.node);
29518     }
29519     Roo.apply(this, cfg);
29520      
29521     
29522     
29523 }
29524 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29525  
29526     node : false,
29527     
29528     width: '',
29529     textAlign : 'left',
29530     valign : 'top',
29531     
29532     colspan : 1,
29533     rowspan : 1,
29534     
29535     
29536     // used by context menu
29537     friendly_name : 'Table Cell',
29538     deleteTitle : false, // use our customer delete
29539     
29540     // context menu is drawn once..
29541     
29542     contextMenu : function(toolbar)
29543     {
29544         
29545         var cell = function() {
29546             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29547         };
29548         
29549         var table = function() {
29550             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
29551         };
29552         
29553         var lr = false;
29554         var saveSel = function()
29555         {
29556             lr = toolbar.editorcore.getSelection().getRangeAt(0);
29557         }
29558         var restoreSel = function()
29559         {
29560             if (lr) {
29561                 (function() {
29562                     toolbar.editorcore.focus();
29563                     var cr = toolbar.editorcore.getSelection();
29564                     cr.removeAllRanges();
29565                     cr.addRange(lr);
29566                     toolbar.editorcore.onEditorEvent();
29567                 }).defer(10, this);
29568                 
29569                 
29570             }
29571         }
29572         
29573         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29574         
29575         var syncValue = toolbar.editorcore.syncValue;
29576         
29577         var fields = {};
29578         
29579         return [
29580             {
29581                 xtype : 'Button',
29582                 text : 'Edit Table',
29583                 listeners : {
29584                     click : function() {
29585                         var t = toolbar.tb.selectedNode.closest('table');
29586                         toolbar.editorcore.selectNode(t);
29587                         toolbar.editorcore.onEditorEvent();                        
29588                     }
29589                 }
29590                 
29591             },
29592               
29593            
29594              
29595             {
29596                 xtype : 'TextItem',
29597                 text : "Column Width: ",
29598                  xns : rooui.Toolbar 
29599                
29600             },
29601             {
29602                 xtype : 'Button',
29603                 text: '-',
29604                 listeners : {
29605                     click : function (_self, e)
29606                     {
29607                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29608                         cell().shrinkColumn();
29609                         syncValue();
29610                          toolbar.editorcore.onEditorEvent();
29611                     }
29612                 },
29613                 xns : rooui.Toolbar
29614             },
29615             {
29616                 xtype : 'Button',
29617                 text: '+',
29618                 listeners : {
29619                     click : function (_self, e)
29620                     {
29621                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29622                         cell().growColumn();
29623                         syncValue();
29624                         toolbar.editorcore.onEditorEvent();
29625                     }
29626                 },
29627                 xns : rooui.Toolbar
29628             },
29629             
29630             {
29631                 xtype : 'TextItem',
29632                 text : "Vertical Align: ",
29633                 xns : rooui.Toolbar  //Boostrap?
29634             },
29635             {
29636                 xtype : 'ComboBox',
29637                 allowBlank : false,
29638                 displayField : 'val',
29639                 editable : true,
29640                 listWidth : 100,
29641                 triggerAction : 'all',
29642                 typeAhead : true,
29643                 valueField : 'val',
29644                 width : 100,
29645                 name : 'valign',
29646                 listeners : {
29647                     select : function (combo, r, index)
29648                     {
29649                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29650                         var b = cell();
29651                         b.valign = r.get('val');
29652                         b.updateElement();
29653                         syncValue();
29654                         toolbar.editorcore.onEditorEvent();
29655                     }
29656                 },
29657                 xns : rooui.form,
29658                 store : {
29659                     xtype : 'SimpleStore',
29660                     data : [
29661                         ['top'],
29662                         ['middle'],
29663                         ['bottom'] // there are afew more... 
29664                     ],
29665                     fields : [ 'val'],
29666                     xns : Roo.data
29667                 }
29668             },
29669             
29670             {
29671                 xtype : 'TextItem',
29672                 text : "Merge Cells: ",
29673                  xns : rooui.Toolbar 
29674                
29675             },
29676             
29677             
29678             {
29679                 xtype : 'Button',
29680                 text: 'Right',
29681                 listeners : {
29682                     click : function (_self, e)
29683                     {
29684                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29685                         cell().mergeRight();
29686                         //block().growColumn();
29687                         syncValue();
29688                         toolbar.editorcore.onEditorEvent();
29689                     }
29690                 },
29691                 xns : rooui.Toolbar
29692             },
29693              
29694             {
29695                 xtype : 'Button',
29696                 text: 'Below',
29697                 listeners : {
29698                     click : function (_self, e)
29699                     {
29700                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29701                         cell().mergeBelow();
29702                         //block().growColumn();
29703                         syncValue();
29704                         toolbar.editorcore.onEditorEvent();
29705                     }
29706                 },
29707                 xns : rooui.Toolbar
29708             },
29709             {
29710                 xtype : 'TextItem',
29711                 text : "| ",
29712                  xns : rooui.Toolbar 
29713                
29714             },
29715             
29716             {
29717                 xtype : 'Button',
29718                 text: 'Split',
29719                 listeners : {
29720                     click : function (_self, e)
29721                     {
29722                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29723                         cell().split();
29724                         syncValue();
29725                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29726                         toolbar.editorcore.onEditorEvent();
29727                                              
29728                     }
29729                 },
29730                 xns : rooui.Toolbar
29731             },
29732             {
29733                 xtype : 'Fill',
29734                 xns : rooui.Toolbar 
29735                
29736             },
29737         
29738           
29739             {
29740                 xtype : 'Button',
29741                 text: 'Delete',
29742                  
29743                 xns : rooui.Toolbar,
29744                 menu : {
29745                     xtype : 'Menu',
29746                     xns : rooui.menu,
29747                     items : [
29748                         {
29749                             xtype : 'Item',
29750                             html: 'Column',
29751                             listeners : {
29752                                 click : function (_self, e)
29753                                 {
29754                                     var t = table();
29755                                     
29756                                     cell().deleteColumn();
29757                                     syncValue();
29758                                     toolbar.editorcore.selectNode(t.node);
29759                                     toolbar.editorcore.onEditorEvent();   
29760                                 }
29761                             },
29762                             xns : rooui.menu
29763                         },
29764                         {
29765                             xtype : 'Item',
29766                             html: 'Row',
29767                             listeners : {
29768                                 click : function (_self, e)
29769                                 {
29770                                     var t = table();
29771                                     cell().deleteRow();
29772                                     syncValue();
29773                                     
29774                                     toolbar.editorcore.selectNode(t.node);
29775                                     toolbar.editorcore.onEditorEvent();   
29776                                                          
29777                                 }
29778                             },
29779                             xns : rooui.menu
29780                         },
29781                        {
29782                             xtype : 'Separator',
29783                             xns : rooui.menu
29784                         },
29785                         {
29786                             xtype : 'Item',
29787                             html: 'Table',
29788                             listeners : {
29789                                 click : function (_self, e)
29790                                 {
29791                                     var t = table();
29792                                     var nn = t.node.nextSibling || t.node.previousSibling;
29793                                     t.node.parentNode.removeChild(t.node);
29794                                     if (nn) { 
29795                                         toolbar.editorcore.selectNode(nn, true);
29796                                     }
29797                                     toolbar.editorcore.onEditorEvent();   
29798                                                          
29799                                 }
29800                             },
29801                             xns : rooui.menu
29802                         }
29803                     ]
29804                 }
29805             }
29806             
29807             // align... << fixme
29808             
29809         ];
29810         
29811     },
29812     
29813     
29814   /**
29815      * create a DomHelper friendly object - for use with
29816      * Roo.DomHelper.markup / overwrite / etc..
29817      * ?? should it be called with option to hide all editing features?
29818      */
29819  /**
29820      * create a DomHelper friendly object - for use with
29821      * Roo.DomHelper.markup / overwrite / etc..
29822      * ?? should it be called with option to hide all editing features?
29823      */
29824     toObject : function()
29825     {
29826         var ret = {
29827             tag : 'td',
29828             contenteditable : 'true', // this stops cell selection from picking the table.
29829             'data-block' : 'Td',
29830             valign : this.valign,
29831             style : {  
29832                 'text-align' :  this.textAlign,
29833                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
29834                 'border-collapse' : 'collapse',
29835                 padding : '6px', // 8 for desktop / 4 for mobile
29836                 'vertical-align': this.valign
29837             },
29838             html : this.html
29839         };
29840         if (this.width != '') {
29841             ret.width = this.width;
29842             ret.style.width = this.width;
29843         }
29844         
29845         
29846         if (this.colspan > 1) {
29847             ret.colspan = this.colspan ;
29848         } 
29849         if (this.rowspan > 1) {
29850             ret.rowspan = this.rowspan ;
29851         }
29852         
29853            
29854         
29855         return ret;
29856          
29857     },
29858     
29859     readElement : function(node)
29860     {
29861         node  = node ? node : this.node ;
29862         this.width = node.style.width;
29863         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
29864         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
29865         this.html = node.innerHTML;
29866         if (node.style.textAlign != '') {
29867             this.textAlign = node.style.textAlign;
29868         }
29869         
29870         
29871     },
29872      
29873     // the default cell object... at present...
29874     emptyCell : function() {
29875         return {
29876             colspan :  1,
29877             rowspan :  1,
29878             textAlign : 'left',
29879             html : "&nbsp;" // is this going to be editable now?
29880         };
29881      
29882     },
29883     
29884     removeNode : function()
29885     {
29886         return this.node.closest('table');
29887          
29888     },
29889     
29890     cellData : false,
29891     
29892     colWidths : false,
29893     
29894     toTableArray  : function()
29895     {
29896         var ret = [];
29897         var tab = this.node.closest('tr').closest('table');
29898         Array.from(tab.rows).forEach(function(r, ri){
29899             ret[ri] = [];
29900         });
29901         var rn = 0;
29902         this.colWidths = [];
29903         var all_auto = true;
29904         Array.from(tab.rows).forEach(function(r, ri){
29905             
29906             var cn = 0;
29907             Array.from(r.cells).forEach(function(ce, ci){
29908                 var c =  {
29909                     cell : ce,
29910                     row : rn,
29911                     col: cn,
29912                     colspan : ce.colSpan,
29913                     rowspan : ce.rowSpan
29914                 };
29915                 if (ce.isEqualNode(this.node)) {
29916                     this.cellData = c;
29917                 }
29918                 // if we have been filled up by a row?
29919                 if (typeof(ret[rn][cn]) != 'undefined') {
29920                     while(typeof(ret[rn][cn]) != 'undefined') {
29921                         cn++;
29922                     }
29923                     c.col = cn;
29924                 }
29925                 
29926                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
29927                     this.colWidths[cn] =   ce.style.width;
29928                     if (this.colWidths[cn] != '') {
29929                         all_auto = false;
29930                     }
29931                 }
29932                 
29933                 
29934                 if (c.colspan < 2 && c.rowspan < 2 ) {
29935                     ret[rn][cn] = c;
29936                     cn++;
29937                     return;
29938                 }
29939                 for(var j = 0; j < c.rowspan; j++) {
29940                     if (typeof(ret[rn+j]) == 'undefined') {
29941                         continue; // we have a problem..
29942                     }
29943                     ret[rn+j][cn] = c;
29944                     for(var i = 0; i < c.colspan; i++) {
29945                         ret[rn+j][cn+i] = c;
29946                     }
29947                 }
29948                 
29949                 cn += c.colspan;
29950             }, this);
29951             rn++;
29952         }, this);
29953         
29954         // initalize widths.?
29955         // either all widths or no widths..
29956         if (all_auto) {
29957             this.colWidths[0] = false; // no widths flag.
29958         }
29959         
29960         
29961         return ret;
29962         
29963     },
29964     
29965     
29966     
29967     
29968     mergeRight: function()
29969     {
29970          
29971         // get the contents of the next cell along..
29972         var tr = this.node.closest('tr');
29973         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
29974         if (i >= tr.childNodes.length - 1) {
29975             return; // no cells on right to merge with.
29976         }
29977         var table = this.toTableArray();
29978         
29979         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
29980             return; // nothing right?
29981         }
29982         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
29983         // right cell - must be same rowspan and on the same row.
29984         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
29985             return; // right hand side is not same rowspan.
29986         }
29987         
29988         
29989         
29990         this.node.innerHTML += ' ' + rc.cell.innerHTML;
29991         tr.removeChild(rc.cell);
29992         this.colspan += rc.colspan;
29993         this.node.setAttribute('colspan', this.colspan);
29994
29995         var table = this.toTableArray();
29996         this.normalizeWidths(table);
29997         this.updateWidths(table);
29998     },
29999     
30000     
30001     mergeBelow : function()
30002     {
30003         var table = this.toTableArray();
30004         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30005             return; // no row below
30006         }
30007         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30008             return; // nothing right?
30009         }
30010         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30011         
30012         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30013             return; // right hand side is not same rowspan.
30014         }
30015         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30016         rc.cell.parentNode.removeChild(rc.cell);
30017         this.rowspan += rc.rowspan;
30018         this.node.setAttribute('rowspan', this.rowspan);
30019     },
30020     
30021     split: function()
30022     {
30023         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30024             return;
30025         }
30026         var table = this.toTableArray();
30027         var cd = this.cellData;
30028         this.rowspan = 1;
30029         this.colspan = 1;
30030         
30031         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30032              
30033             
30034             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30035                 if (r == cd.row && c == cd.col) {
30036                     this.node.removeAttribute('rowspan');
30037                     this.node.removeAttribute('colspan');
30038                 }
30039                  
30040                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30041                 ntd.removeAttribute('id'); 
30042                 ntd.style.width  = this.colWidths[c];
30043                 ntd.innerHTML = '';
30044                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30045             }
30046             
30047         }
30048         this.redrawAllCells(table);
30049         
30050     },
30051     
30052     
30053     
30054     redrawAllCells: function(table)
30055     {
30056         
30057          
30058         var tab = this.node.closest('tr').closest('table');
30059         var ctr = tab.rows[0].parentNode;
30060         Array.from(tab.rows).forEach(function(r, ri){
30061             
30062             Array.from(r.cells).forEach(function(ce, ci){
30063                 ce.parentNode.removeChild(ce);
30064             });
30065             r.parentNode.removeChild(r);
30066         });
30067         for(var r = 0 ; r < table.length; r++) {
30068             var re = tab.rows[r];
30069             
30070             var re = tab.ownerDocument.createElement('tr');
30071             ctr.appendChild(re);
30072             for(var c = 0 ; c < table[r].length; c++) {
30073                 if (table[r][c].cell === false) {
30074                     continue;
30075                 }
30076                 
30077                 re.appendChild(table[r][c].cell);
30078                  
30079                 table[r][c].cell = false;
30080             }
30081         }
30082         
30083     },
30084     updateWidths : function(table)
30085     {
30086         for(var r = 0 ; r < table.length; r++) {
30087            
30088             for(var c = 0 ; c < table[r].length; c++) {
30089                 if (table[r][c].cell === false) {
30090                     continue;
30091                 }
30092                 
30093                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30094                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30095                     el.width = Math.floor(this.colWidths[c])  +'%';
30096                     el.updateElement(el.node);
30097                 }
30098                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30099                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30100                     var width = 0;
30101                     for(var i = 0; i < table[r][c].colspan; i ++) {
30102                         width += Math.floor(this.colWidths[c + i]);
30103                     }
30104                     el.width = width  +'%';
30105                     el.updateElement(el.node);
30106                 }
30107                 table[r][c].cell = false; // done
30108             }
30109         }
30110     },
30111     normalizeWidths : function(table)
30112     {
30113         if (this.colWidths[0] === false) {
30114             var nw = 100.0 / this.colWidths.length;
30115             this.colWidths.forEach(function(w,i) {
30116                 this.colWidths[i] = nw;
30117             },this);
30118             return;
30119         }
30120     
30121         var t = 0, missing = [];
30122         
30123         this.colWidths.forEach(function(w,i) {
30124             //if you mix % and
30125             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30126             var add =  this.colWidths[i];
30127             if (add > 0) {
30128                 t+=add;
30129                 return;
30130             }
30131             missing.push(i);
30132             
30133             
30134         },this);
30135         var nc = this.colWidths.length;
30136         if (missing.length) {
30137             var mult = (nc - missing.length) / (1.0 * nc);
30138             var t = mult * t;
30139             var ew = (100 -t) / (1.0 * missing.length);
30140             this.colWidths.forEach(function(w,i) {
30141                 if (w > 0) {
30142                     this.colWidths[i] = w * mult;
30143                     return;
30144                 }
30145                 
30146                 this.colWidths[i] = ew;
30147             }, this);
30148             // have to make up numbers..
30149              
30150         }
30151         // now we should have all the widths..
30152         
30153     
30154     },
30155     
30156     shrinkColumn : function()
30157     {
30158         var table = this.toTableArray();
30159         this.normalizeWidths(table);
30160         var col = this.cellData.col;
30161         var nw = this.colWidths[col] * 0.8;
30162         if (nw < 5) {
30163             return;
30164         }
30165         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30166         this.colWidths.forEach(function(w,i) {
30167             if (i == col) {
30168                  this.colWidths[i] = nw;
30169                 return;
30170             }
30171             this.colWidths[i] += otherAdd
30172         }, this);
30173         this.updateWidths(table);
30174          
30175     },
30176     growColumn : function()
30177     {
30178         var table = this.toTableArray();
30179         this.normalizeWidths(table);
30180         var col = this.cellData.col;
30181         var nw = this.colWidths[col] * 1.2;
30182         if (nw > 90) {
30183             return;
30184         }
30185         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30186         this.colWidths.forEach(function(w,i) {
30187             if (i == col) {
30188                 this.colWidths[i] = nw;
30189                 return;
30190             }
30191             this.colWidths[i] -= otherSub
30192         }, this);
30193         this.updateWidths(table);
30194          
30195     },
30196     deleteRow : function()
30197     {
30198         // delete this rows 'tr'
30199         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30200         // then reduce the rowspan.
30201         var table = this.toTableArray();
30202         // this.cellData.row;
30203         for (var i =0;i< table[this.cellData.row].length ; i++) {
30204             var c = table[this.cellData.row][i];
30205             if (c.row != this.cellData.row) {
30206                 
30207                 c.rowspan--;
30208                 c.cell.setAttribute('rowspan', c.rowspan);
30209                 continue;
30210             }
30211             if (c.rowspan > 1) {
30212                 c.rowspan--;
30213                 c.cell.setAttribute('rowspan', c.rowspan);
30214             }
30215         }
30216         table.splice(this.cellData.row,1);
30217         this.redrawAllCells(table);
30218         
30219     },
30220     deleteColumn : function()
30221     {
30222         var table = this.toTableArray();
30223         
30224         for (var i =0;i< table.length ; i++) {
30225             var c = table[i][this.cellData.col];
30226             if (c.col != this.cellData.col) {
30227                 table[i][this.cellData.col].colspan--;
30228             } else if (c.colspan > 1) {
30229                 c.colspan--;
30230                 c.cell.setAttribute('colspan', c.colspan);
30231             }
30232             table[i].splice(this.cellData.col,1);
30233         }
30234         
30235         this.redrawAllCells(table);
30236     }
30237     
30238     
30239     
30240     
30241 })
30242
30243 //<script type="text/javascript">
30244
30245 /*
30246  * Based  Ext JS Library 1.1.1
30247  * Copyright(c) 2006-2007, Ext JS, LLC.
30248  * LGPL
30249  *
30250  */
30251  
30252 /**
30253  * @class Roo.HtmlEditorCore
30254  * @extends Roo.Component
30255  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30256  *
30257  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30258  */
30259
30260 Roo.HtmlEditorCore = function(config){
30261     
30262     
30263     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30264     
30265     
30266     this.addEvents({
30267         /**
30268          * @event initialize
30269          * Fires when the editor is fully initialized (including the iframe)
30270          * @param {Roo.HtmlEditorCore} this
30271          */
30272         initialize: true,
30273         /**
30274          * @event activate
30275          * Fires when the editor is first receives the focus. Any insertion must wait
30276          * until after this event.
30277          * @param {Roo.HtmlEditorCore} this
30278          */
30279         activate: true,
30280          /**
30281          * @event beforesync
30282          * Fires before the textarea is updated with content from the editor iframe. Return false
30283          * to cancel the sync.
30284          * @param {Roo.HtmlEditorCore} this
30285          * @param {String} html
30286          */
30287         beforesync: true,
30288          /**
30289          * @event beforepush
30290          * Fires before the iframe editor is updated with content from the textarea. Return false
30291          * to cancel the push.
30292          * @param {Roo.HtmlEditorCore} this
30293          * @param {String} html
30294          */
30295         beforepush: true,
30296          /**
30297          * @event sync
30298          * Fires when the textarea is updated with content from the editor iframe.
30299          * @param {Roo.HtmlEditorCore} this
30300          * @param {String} html
30301          */
30302         sync: true,
30303          /**
30304          * @event push
30305          * Fires when the iframe editor is updated with content from the textarea.
30306          * @param {Roo.HtmlEditorCore} this
30307          * @param {String} html
30308          */
30309         push: true,
30310         
30311         /**
30312          * @event editorevent
30313          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30314          * @param {Roo.HtmlEditorCore} this
30315          */
30316         editorevent: true 
30317          
30318         
30319     });
30320     
30321     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30322     
30323     // defaults : white / black...
30324     this.applyBlacklists();
30325     
30326     
30327     
30328 };
30329
30330
30331 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30332
30333
30334      /**
30335      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30336      */
30337     
30338     owner : false,
30339     
30340      /**
30341      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30342      *                        Roo.resizable.
30343      */
30344     resizable : false,
30345      /**
30346      * @cfg {Number} height (in pixels)
30347      */   
30348     height: 300,
30349    /**
30350      * @cfg {Number} width (in pixels)
30351      */   
30352     width: 500,
30353      /**
30354      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30355      *         if you are doing an email editor, this probably needs disabling, it's designed
30356      */
30357     autoClean: true,
30358     
30359     /**
30360      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30361      */
30362     enableBlocks : true,
30363     /**
30364      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30365      * 
30366      */
30367     stylesheets: false,
30368      /**
30369      * @cfg {String} language default en - language of text (usefull for rtl languages)
30370      * 
30371      */
30372     language: 'en',
30373     
30374     /**
30375      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30376      *          - by default they are stripped - if you are editing email you may need this.
30377      */
30378     allowComments: false,
30379     // id of frame..
30380     frameId: false,
30381     
30382     // private properties
30383     validationEvent : false,
30384     deferHeight: true,
30385     initialized : false,
30386     activated : false,
30387     sourceEditMode : false,
30388     onFocus : Roo.emptyFn,
30389     iframePad:3,
30390     hideMode:'offsets',
30391     
30392     clearUp: true,
30393     
30394     // blacklist + whitelisted elements..
30395     black: false,
30396     white: false,
30397      
30398     bodyCls : '',
30399
30400     
30401     undoManager : false,
30402     /**
30403      * Protected method that will not generally be called directly. It
30404      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30405      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30406      */
30407     getDocMarkup : function(){
30408         // body styles..
30409         var st = '';
30410         
30411         // inherit styels from page...?? 
30412         if (this.stylesheets === false) {
30413             
30414             Roo.get(document.head).select('style').each(function(node) {
30415                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30416             });
30417             
30418             Roo.get(document.head).select('link').each(function(node) { 
30419                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30420             });
30421             
30422         } else if (!this.stylesheets.length) {
30423                 // simple..
30424                 st = '<style type="text/css">' +
30425                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30426                    '</style>';
30427         } else {
30428             for (var i in this.stylesheets) {
30429                 if (typeof(this.stylesheets[i]) != 'string') {
30430                     continue;
30431                 }
30432                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30433             }
30434             
30435         }
30436         
30437         st +=  '<style type="text/css">' +
30438             'IMG { cursor: pointer } ' +
30439         '</style>';
30440         
30441         st += '<meta name="google" content="notranslate">';
30442         
30443         var cls = 'notranslate roo-htmleditor-body';
30444         
30445         if(this.bodyCls.length){
30446             cls += ' ' + this.bodyCls;
30447         }
30448         
30449         return '<html  class="notranslate" translate="no"><head>' + st  +
30450             //<style type="text/css">' +
30451             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30452             //'</style>' +
30453             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30454     },
30455
30456     // private
30457     onRender : function(ct, position)
30458     {
30459         var _t = this;
30460         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30461         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30462         
30463         
30464         this.el.dom.style.border = '0 none';
30465         this.el.dom.setAttribute('tabIndex', -1);
30466         this.el.addClass('x-hidden hide');
30467         
30468         
30469         
30470         if(Roo.isIE){ // fix IE 1px bogus margin
30471             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30472         }
30473        
30474         
30475         this.frameId = Roo.id();
30476         
30477          
30478         
30479         var iframe = this.owner.wrap.createChild({
30480             tag: 'iframe',
30481             cls: 'form-control', // bootstrap..
30482             id: this.frameId,
30483             name: this.frameId,
30484             frameBorder : 'no',
30485             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
30486         }, this.el
30487         );
30488         
30489         
30490         this.iframe = iframe.dom;
30491
30492         this.assignDocWin();
30493         
30494         this.doc.designMode = 'on';
30495        
30496         this.doc.open();
30497         this.doc.write(this.getDocMarkup());
30498         this.doc.close();
30499
30500         
30501         var task = { // must defer to wait for browser to be ready
30502             run : function(){
30503                 //console.log("run task?" + this.doc.readyState);
30504                 this.assignDocWin();
30505                 if(this.doc.body || this.doc.readyState == 'complete'){
30506                     try {
30507                         this.doc.designMode="on";
30508                         
30509                     } catch (e) {
30510                         return;
30511                     }
30512                     Roo.TaskMgr.stop(task);
30513                     this.initEditor.defer(10, this);
30514                 }
30515             },
30516             interval : 10,
30517             duration: 10000,
30518             scope: this
30519         };
30520         Roo.TaskMgr.start(task);
30521
30522     },
30523
30524     // private
30525     onResize : function(w, h)
30526     {
30527          Roo.log('resize: ' +w + ',' + h );
30528         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30529         if(!this.iframe){
30530             return;
30531         }
30532         if(typeof w == 'number'){
30533             
30534             this.iframe.style.width = w + 'px';
30535         }
30536         if(typeof h == 'number'){
30537             
30538             this.iframe.style.height = h + 'px';
30539             if(this.doc){
30540                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
30541             }
30542         }
30543         
30544     },
30545
30546     /**
30547      * Toggles the editor between standard and source edit mode.
30548      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30549      */
30550     toggleSourceEdit : function(sourceEditMode){
30551         
30552         this.sourceEditMode = sourceEditMode === true;
30553         
30554         if(this.sourceEditMode){
30555  
30556             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
30557             
30558         }else{
30559             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
30560             //this.iframe.className = '';
30561             this.deferFocus();
30562         }
30563         //this.setSize(this.owner.wrap.getSize());
30564         //this.fireEvent('editmodechange', this, this.sourceEditMode);
30565     },
30566
30567     
30568   
30569
30570     /**
30571      * Protected method that will not generally be called directly. If you need/want
30572      * custom HTML cleanup, this is the method you should override.
30573      * @param {String} html The HTML to be cleaned
30574      * return {String} The cleaned HTML
30575      */
30576     cleanHtml : function(html)
30577     {
30578         html = String(html);
30579         if(html.length > 5){
30580             if(Roo.isSafari){ // strip safari nonsense
30581                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
30582             }
30583         }
30584         if(html == '&nbsp;'){
30585             html = '';
30586         }
30587         return html;
30588     },
30589
30590     /**
30591      * HTML Editor -> Textarea
30592      * Protected method that will not generally be called directly. Syncs the contents
30593      * of the editor iframe with the textarea.
30594      */
30595     syncValue : function()
30596     {
30597         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
30598         if(this.initialized){
30599             
30600             if (this.undoManager) {
30601                 this.undoManager.addEvent();
30602             }
30603
30604             
30605             var bd = (this.doc.body || this.doc.documentElement);
30606            
30607             
30608             var sel = this.win.getSelection();
30609             
30610             var div = document.createElement('div');
30611             div.innerHTML = bd.innerHTML;
30612             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
30613             if (gtx.length > 0) {
30614                 var rm = gtx.item(0).parentNode;
30615                 rm.parentNode.removeChild(rm);
30616             }
30617             
30618            
30619             if (this.enableBlocks) {
30620                 new Roo.htmleditor.FilterBlock({ node : div });
30621             }
30622             
30623             var html = div.innerHTML;
30624             
30625             //?? tidy?
30626             if (this.autoClean) {
30627                 
30628                 new Roo.htmleditor.FilterAttributes({
30629                     node : div,
30630                     attrib_white : [
30631                             'href',
30632                             'src',
30633                             'name',
30634                             'align',
30635                             'colspan',
30636                             'rowspan',
30637                             'data-display',
30638                             'data-width',
30639                             'start' ,
30640                             'style',
30641                             // youtube embed.
30642                             'class',
30643                             'allowfullscreen',
30644                             'frameborder',
30645                             'width',
30646                             'height',
30647                             'alt'
30648                             ],
30649                     attrib_clean : ['href', 'src' ] 
30650                 });
30651                 
30652                 var tidy = new Roo.htmleditor.TidySerializer({
30653                     inner:  true
30654                 });
30655                 html  = tidy.serialize(div);
30656                 
30657             }
30658             
30659             
30660             if(Roo.isSafari){
30661                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
30662                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
30663                 if(m && m[1]){
30664                     html = '<div style="'+m[0]+'">' + html + '</div>';
30665                 }
30666             }
30667             html = this.cleanHtml(html);
30668             // fix up the special chars.. normaly like back quotes in word...
30669             // however we do not want to do this with chinese..
30670             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
30671                 
30672                 var cc = match.charCodeAt();
30673
30674                 // Get the character value, handling surrogate pairs
30675                 if (match.length == 2) {
30676                     // It's a surrogate pair, calculate the Unicode code point
30677                     var high = match.charCodeAt(0) - 0xD800;
30678                     var low  = match.charCodeAt(1) - 0xDC00;
30679                     cc = (high * 0x400) + low + 0x10000;
30680                 }  else if (
30681                     (cc >= 0x4E00 && cc < 0xA000 ) ||
30682                     (cc >= 0x3400 && cc < 0x4E00 ) ||
30683                     (cc >= 0xf900 && cc < 0xfb00 )
30684                 ) {
30685                         return match;
30686                 }  
30687          
30688                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
30689                 return "&#" + cc + ";";
30690                 
30691                 
30692             });
30693             
30694             
30695              
30696             if(this.owner.fireEvent('beforesync', this, html) !== false){
30697                 this.el.dom.value = html;
30698                 this.owner.fireEvent('sync', this, html);
30699             }
30700         }
30701     },
30702
30703     /**
30704      * TEXTAREA -> EDITABLE
30705      * Protected method that will not generally be called directly. Pushes the value of the textarea
30706      * into the iframe editor.
30707      */
30708     pushValue : function()
30709     {
30710         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
30711         if(this.initialized){
30712             var v = this.el.dom.value.trim();
30713             
30714             
30715             if(this.owner.fireEvent('beforepush', this, v) !== false){
30716                 var d = (this.doc.body || this.doc.documentElement);
30717                 d.innerHTML = v;
30718                  
30719                 this.el.dom.value = d.innerHTML;
30720                 this.owner.fireEvent('push', this, v);
30721             }
30722             if (this.autoClean) {
30723                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
30724                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
30725             }
30726             if (this.enableBlocks) {
30727                 Roo.htmleditor.Block.initAll(this.doc.body);
30728             }
30729             
30730             this.updateLanguage();
30731             
30732             var lc = this.doc.body.lastChild;
30733             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
30734                 // add an extra line at the end.
30735                 this.doc.body.appendChild(this.doc.createElement('br'));
30736             }
30737             
30738             
30739         }
30740     },
30741
30742     // private
30743     deferFocus : function(){
30744         this.focus.defer(10, this);
30745     },
30746
30747     // doc'ed in Field
30748     focus : function(){
30749         if(this.win && !this.sourceEditMode){
30750             this.win.focus();
30751         }else{
30752             this.el.focus();
30753         }
30754     },
30755     
30756     assignDocWin: function()
30757     {
30758         var iframe = this.iframe;
30759         
30760          if(Roo.isIE){
30761             this.doc = iframe.contentWindow.document;
30762             this.win = iframe.contentWindow;
30763         } else {
30764 //            if (!Roo.get(this.frameId)) {
30765 //                return;
30766 //            }
30767 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30768 //            this.win = Roo.get(this.frameId).dom.contentWindow;
30769             
30770             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
30771                 return;
30772             }
30773             
30774             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30775             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
30776         }
30777     },
30778     
30779     // private
30780     initEditor : function(){
30781         //console.log("INIT EDITOR");
30782         this.assignDocWin();
30783         
30784         
30785         
30786         this.doc.designMode="on";
30787         this.doc.open();
30788         this.doc.write(this.getDocMarkup());
30789         this.doc.close();
30790         
30791         var dbody = (this.doc.body || this.doc.documentElement);
30792         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
30793         // this copies styles from the containing element into thsi one..
30794         // not sure why we need all of this..
30795         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
30796         
30797         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
30798         //ss['background-attachment'] = 'fixed'; // w3c
30799         dbody.bgProperties = 'fixed'; // ie
30800         dbody.setAttribute("translate", "no");
30801         
30802         //Roo.DomHelper.applyStyles(dbody, ss);
30803         Roo.EventManager.on(this.doc, {
30804              
30805             'mouseup': this.onEditorEvent,
30806             'dblclick': this.onEditorEvent,
30807             'click': this.onEditorEvent,
30808             'keyup': this.onEditorEvent,
30809             
30810             buffer:100,
30811             scope: this
30812         });
30813         Roo.EventManager.on(this.doc, {
30814             'paste': this.onPasteEvent,
30815             scope : this
30816         });
30817         if(Roo.isGecko){
30818             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
30819         }
30820         //??? needed???
30821         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
30822             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
30823         }
30824         this.initialized = true;
30825
30826         
30827         // initialize special key events - enter
30828         new Roo.htmleditor.KeyEnter({core : this});
30829         
30830          
30831         
30832         this.owner.fireEvent('initialize', this);
30833         this.pushValue();
30834     },
30835     // this is to prevent a href clicks resulting in a redirect?
30836    
30837     onPasteEvent : function(e,v)
30838     {
30839         // I think we better assume paste is going to be a dirty load of rubish from word..
30840         
30841         // even pasting into a 'email version' of this widget will have to clean up that mess.
30842         var cd = (e.browserEvent.clipboardData || window.clipboardData);
30843         
30844         // check what type of paste - if it's an image, then handle it differently.
30845         if (cd.files && cd.files.length > 0) {
30846             // pasting images?
30847             var urlAPI = (window.createObjectURL && window) || 
30848                 (window.URL && URL.revokeObjectURL && URL) || 
30849                 (window.webkitURL && webkitURL);
30850     
30851             var url = urlAPI.createObjectURL( cd.files[0]);
30852             this.insertAtCursor('<img src=" + url + ">');
30853             return false;
30854         }
30855         if (cd.types.indexOf('text/html') < 0 ) {
30856             return false;
30857         }
30858         var images = [];
30859         var html = cd.getData('text/html'); // clipboard event
30860         if (cd.types.indexOf('text/rtf') > -1) {
30861             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
30862             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
30863         }
30864         //Roo.log(images);
30865         //Roo.log(imgs);
30866         // fixme..
30867         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
30868                        .map(function(g) { return g.toDataURL(); })
30869                        .filter(function(g) { return g != 'about:blank'; });
30870         
30871         //Roo.log(html);
30872         html = this.cleanWordChars(html);
30873         
30874         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
30875         
30876         
30877         var sn = this.getParentElement();
30878         // check if d contains a table, and prevent nesting??
30879         //Roo.log(d.getElementsByTagName('table'));
30880         //Roo.log(sn);
30881         //Roo.log(sn.closest('table'));
30882         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
30883             e.preventDefault();
30884             this.insertAtCursor("You can not nest tables");
30885             //Roo.log("prevent?"); // fixme - 
30886             return false;
30887         }
30888         
30889         
30890         
30891         if (images.length > 0) {
30892             // replace all v:imagedata - with img.
30893             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
30894             Roo.each(ar, function(node) {
30895                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
30896                 node.parentNode.removeChild(node);
30897             });
30898             
30899             
30900             Roo.each(d.getElementsByTagName('img'), function(img, i) {
30901                 img.setAttribute('src', images[i]);
30902             });
30903         }
30904         if (this.autoClean) {
30905             new Roo.htmleditor.FilterWord({ node : d });
30906             
30907             new Roo.htmleditor.FilterStyleToTag({ node : d });
30908             new Roo.htmleditor.FilterAttributes({
30909                 node : d,
30910                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30911                 attrib_clean : ['href', 'src' ] 
30912             });
30913             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
30914             // should be fonts..
30915             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
30916             new Roo.htmleditor.FilterParagraph({ node : d });
30917             new Roo.htmleditor.FilterSpan({ node : d });
30918             new Roo.htmleditor.FilterLongBr({ node : d });
30919             new Roo.htmleditor.FilterComment({ node : d });
30920             
30921             
30922         }
30923         if (this.enableBlocks) {
30924                 
30925             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
30926                 if (img.closest('figure')) { // assume!! that it's aready
30927                     return;
30928                 }
30929                 var fig  = new Roo.htmleditor.BlockFigure({
30930                     image_src  : img.src
30931                 });
30932                 fig.updateElement(img); // replace it..
30933                 
30934             });
30935         }
30936         
30937         
30938         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
30939         if (this.enableBlocks) {
30940             Roo.htmleditor.Block.initAll(this.doc.body);
30941         }
30942          
30943         
30944         e.preventDefault();
30945         return false;
30946         // default behaveiour should be our local cleanup paste? (optional?)
30947         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
30948         //this.owner.fireEvent('paste', e, v);
30949     },
30950     // private
30951     onDestroy : function(){
30952         
30953         
30954         
30955         if(this.rendered){
30956             
30957             //for (var i =0; i < this.toolbars.length;i++) {
30958             //    // fixme - ask toolbars for heights?
30959             //    this.toolbars[i].onDestroy();
30960            // }
30961             
30962             //this.wrap.dom.innerHTML = '';
30963             //this.wrap.remove();
30964         }
30965     },
30966
30967     // private
30968     onFirstFocus : function(){
30969         
30970         this.assignDocWin();
30971         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
30972         
30973         this.activated = true;
30974          
30975     
30976         if(Roo.isGecko){ // prevent silly gecko errors
30977             this.win.focus();
30978             var s = this.win.getSelection();
30979             if(!s.focusNode || s.focusNode.nodeType != 3){
30980                 var r = s.getRangeAt(0);
30981                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
30982                 r.collapse(true);
30983                 this.deferFocus();
30984             }
30985             try{
30986                 this.execCmd('useCSS', true);
30987                 this.execCmd('styleWithCSS', false);
30988             }catch(e){}
30989         }
30990         this.owner.fireEvent('activate', this);
30991     },
30992
30993     // private
30994     adjustFont: function(btn){
30995         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
30996         //if(Roo.isSafari){ // safari
30997         //    adjust *= 2;
30998        // }
30999         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31000         if(Roo.isSafari){ // safari
31001             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31002             v =  (v < 10) ? 10 : v;
31003             v =  (v > 48) ? 48 : v;
31004             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31005             
31006         }
31007         
31008         
31009         v = Math.max(1, v+adjust);
31010         
31011         this.execCmd('FontSize', v  );
31012     },
31013
31014     onEditorEvent : function(e)
31015     {
31016          
31017         
31018         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31019             return; // we do not handle this.. (undo manager does..)
31020         }
31021         // in theory this detects if the last element is not a br, then we try and do that.
31022         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31023         if (e &&
31024             e.target.nodeName == 'BODY' &&
31025             e.type == "mouseup" &&
31026             this.doc.body.lastChild
31027            ) {
31028             var lc = this.doc.body.lastChild;
31029             // gtx-trans is google translate plugin adding crap.
31030             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31031                 lc = lc.previousSibling;
31032             }
31033             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31034             // if last element is <BR> - then dont do anything.
31035             
31036                 var ns = this.doc.createElement('br');
31037                 this.doc.body.appendChild(ns);
31038                 range = this.doc.createRange();
31039                 range.setStartAfter(ns);
31040                 range.collapse(true);
31041                 var sel = this.win.getSelection();
31042                 sel.removeAllRanges();
31043                 sel.addRange(range);
31044             }
31045         }
31046         
31047         
31048         
31049         this.fireEditorEvent(e);
31050       //  this.updateToolbar();
31051         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31052     },
31053     
31054     fireEditorEvent: function(e)
31055     {
31056         this.owner.fireEvent('editorevent', this, e);
31057     },
31058
31059     insertTag : function(tg)
31060     {
31061         // could be a bit smarter... -> wrap the current selected tRoo..
31062         if (tg.toLowerCase() == 'span' ||
31063             tg.toLowerCase() == 'code' ||
31064             tg.toLowerCase() == 'sup' ||
31065             tg.toLowerCase() == 'sub' 
31066             ) {
31067             
31068             range = this.createRange(this.getSelection());
31069             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31070             wrappingNode.appendChild(range.extractContents());
31071             range.insertNode(wrappingNode);
31072
31073             return;
31074             
31075             
31076             
31077         }
31078         this.execCmd("formatblock",   tg);
31079         this.undoManager.addEvent(); 
31080     },
31081     
31082     insertText : function(txt)
31083     {
31084         
31085         
31086         var range = this.createRange();
31087         range.deleteContents();
31088                //alert(Sender.getAttribute('label'));
31089                
31090         range.insertNode(this.doc.createTextNode(txt));
31091         this.undoManager.addEvent();
31092     } ,
31093     
31094      
31095
31096     /**
31097      * Executes a Midas editor command on the editor document and performs necessary focus and
31098      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31099      * @param {String} cmd The Midas command
31100      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31101      */
31102     relayCmd : function(cmd, value)
31103     {
31104         
31105         switch (cmd) {
31106             case 'justifyleft':
31107             case 'justifyright':
31108             case 'justifycenter':
31109                 // if we are in a cell, then we will adjust the
31110                 var n = this.getParentElement();
31111                 var td = n.closest('td');
31112                 if (td) {
31113                     var bl = Roo.htmleditor.Block.factory(td);
31114                     bl.textAlign = cmd.replace('justify','');
31115                     bl.updateElement();
31116                     this.owner.fireEvent('editorevent', this);
31117                     return;
31118                 }
31119                 this.execCmd('styleWithCSS', true); // 
31120                 break;
31121             case 'bold':
31122             case 'italic':
31123                 // if there is no selection, then we insert, and set the curson inside it..
31124                 this.execCmd('styleWithCSS', false); 
31125                 break;
31126                 
31127         
31128             default:
31129                 break;
31130         }
31131         
31132         
31133         this.win.focus();
31134         this.execCmd(cmd, value);
31135         this.owner.fireEvent('editorevent', this);
31136         //this.updateToolbar();
31137         this.owner.deferFocus();
31138     },
31139
31140     /**
31141      * Executes a Midas editor command directly on the editor document.
31142      * For visual commands, you should use {@link #relayCmd} instead.
31143      * <b>This should only be called after the editor is initialized.</b>
31144      * @param {String} cmd The Midas command
31145      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31146      */
31147     execCmd : function(cmd, value){
31148         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31149         this.syncValue();
31150     },
31151  
31152  
31153    
31154     /**
31155      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31156      * to insert tRoo.
31157      * @param {String} text | dom node.. 
31158      */
31159     insertAtCursor : function(text)
31160     {
31161         
31162         if(!this.activated){
31163             return;
31164         }
31165          
31166         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31167             this.win.focus();
31168             
31169             
31170             // from jquery ui (MIT licenced)
31171             var range, node;
31172             var win = this.win;
31173             
31174             if (win.getSelection && win.getSelection().getRangeAt) {
31175                 
31176                 // delete the existing?
31177                 
31178                 this.createRange(this.getSelection()).deleteContents();
31179                 range = win.getSelection().getRangeAt(0);
31180                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31181                 range.insertNode(node);
31182                 range = range.cloneRange();
31183                 range.collapse(false);
31184                  
31185                 win.getSelection().removeAllRanges();
31186                 win.getSelection().addRange(range);
31187                 
31188                 
31189                 
31190             } else if (win.document.selection && win.document.selection.createRange) {
31191                 // no firefox support
31192                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31193                 win.document.selection.createRange().pasteHTML(txt);
31194             
31195             } else {
31196                 // no firefox support
31197                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31198                 this.execCmd('InsertHTML', txt);
31199             } 
31200             this.syncValue();
31201             
31202             this.deferFocus();
31203         }
31204     },
31205  // private
31206     mozKeyPress : function(e){
31207         if(e.ctrlKey){
31208             var c = e.getCharCode(), cmd;
31209           
31210             if(c > 0){
31211                 c = String.fromCharCode(c).toLowerCase();
31212                 switch(c){
31213                     case 'b':
31214                         cmd = 'bold';
31215                         break;
31216                     case 'i':
31217                         cmd = 'italic';
31218                         break;
31219                     
31220                     case 'u':
31221                         cmd = 'underline';
31222                         break;
31223                     
31224                     //case 'v':
31225                       //  this.cleanUpPaste.defer(100, this);
31226                       //  return;
31227                         
31228                 }
31229                 if(cmd){
31230                     
31231                     this.relayCmd(cmd);
31232                     //this.win.focus();
31233                     //this.execCmd(cmd);
31234                     //this.deferFocus();
31235                     e.preventDefault();
31236                 }
31237                 
31238             }
31239         }
31240     },
31241
31242     // private
31243     fixKeys : function(){ // load time branching for fastest keydown performance
31244         
31245         
31246         if(Roo.isIE){
31247             return function(e){
31248                 var k = e.getKey(), r;
31249                 if(k == e.TAB){
31250                     e.stopEvent();
31251                     r = this.doc.selection.createRange();
31252                     if(r){
31253                         r.collapse(true);
31254                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31255                         this.deferFocus();
31256                     }
31257                     return;
31258                 }
31259                 /// this is handled by Roo.htmleditor.KeyEnter
31260                  /*
31261                 if(k == e.ENTER){
31262                     r = this.doc.selection.createRange();
31263                     if(r){
31264                         var target = r.parentElement();
31265                         if(!target || target.tagName.toLowerCase() != 'li'){
31266                             e.stopEvent();
31267                             r.pasteHTML('<br/>');
31268                             r.collapse(false);
31269                             r.select();
31270                         }
31271                     }
31272                 }
31273                 */
31274                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31275                 //    this.cleanUpPaste.defer(100, this);
31276                 //    return;
31277                 //}
31278                 
31279                 
31280             };
31281         }else if(Roo.isOpera){
31282             return function(e){
31283                 var k = e.getKey();
31284                 if(k == e.TAB){
31285                     e.stopEvent();
31286                     this.win.focus();
31287                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31288                     this.deferFocus();
31289                 }
31290                
31291                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31292                 //    this.cleanUpPaste.defer(100, this);
31293                  //   return;
31294                 //}
31295                 
31296             };
31297         }else if(Roo.isSafari){
31298             return function(e){
31299                 var k = e.getKey();
31300                 
31301                 if(k == e.TAB){
31302                     e.stopEvent();
31303                     this.execCmd('InsertText','\t');
31304                     this.deferFocus();
31305                     return;
31306                 }
31307                  this.mozKeyPress(e);
31308                 
31309                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31310                  //   this.cleanUpPaste.defer(100, this);
31311                  //   return;
31312                // }
31313                 
31314              };
31315         }
31316     }(),
31317     
31318     getAllAncestors: function()
31319     {
31320         var p = this.getSelectedNode();
31321         var a = [];
31322         if (!p) {
31323             a.push(p); // push blank onto stack..
31324             p = this.getParentElement();
31325         }
31326         
31327         
31328         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31329             a.push(p);
31330             p = p.parentNode;
31331         }
31332         a.push(this.doc.body);
31333         return a;
31334     },
31335     lastSel : false,
31336     lastSelNode : false,
31337     
31338     
31339     getSelection : function() 
31340     {
31341         this.assignDocWin();
31342         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31343     },
31344     /**
31345      * Select a dom node
31346      * @param {DomElement} node the node to select
31347      */
31348     selectNode : function(node, collapse)
31349     {
31350         var nodeRange = node.ownerDocument.createRange();
31351         try {
31352             nodeRange.selectNode(node);
31353         } catch (e) {
31354             nodeRange.selectNodeContents(node);
31355         }
31356         if (collapse === true) {
31357             nodeRange.collapse(true);
31358         }
31359         //
31360         var s = this.win.getSelection();
31361         s.removeAllRanges();
31362         s.addRange(nodeRange);
31363     },
31364     
31365     getSelectedNode: function() 
31366     {
31367         // this may only work on Gecko!!!
31368         
31369         // should we cache this!!!!
31370         
31371          
31372          
31373         var range = this.createRange(this.getSelection()).cloneRange();
31374         
31375         if (Roo.isIE) {
31376             var parent = range.parentElement();
31377             while (true) {
31378                 var testRange = range.duplicate();
31379                 testRange.moveToElementText(parent);
31380                 if (testRange.inRange(range)) {
31381                     break;
31382                 }
31383                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31384                     break;
31385                 }
31386                 parent = parent.parentElement;
31387             }
31388             return parent;
31389         }
31390         
31391         // is ancestor a text element.
31392         var ac =  range.commonAncestorContainer;
31393         if (ac.nodeType == 3) {
31394             ac = ac.parentNode;
31395         }
31396         
31397         var ar = ac.childNodes;
31398          
31399         var nodes = [];
31400         var other_nodes = [];
31401         var has_other_nodes = false;
31402         for (var i=0;i<ar.length;i++) {
31403             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31404                 continue;
31405             }
31406             // fullly contained node.
31407             
31408             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31409                 nodes.push(ar[i]);
31410                 continue;
31411             }
31412             
31413             // probably selected..
31414             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31415                 other_nodes.push(ar[i]);
31416                 continue;
31417             }
31418             // outer..
31419             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31420                 continue;
31421             }
31422             
31423             
31424             has_other_nodes = true;
31425         }
31426         if (!nodes.length && other_nodes.length) {
31427             nodes= other_nodes;
31428         }
31429         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31430             return false;
31431         }
31432         
31433         return nodes[0];
31434     },
31435     
31436     
31437     createRange: function(sel)
31438     {
31439         // this has strange effects when using with 
31440         // top toolbar - not sure if it's a great idea.
31441         //this.editor.contentWindow.focus();
31442         if (typeof sel != "undefined") {
31443             try {
31444                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31445             } catch(e) {
31446                 return this.doc.createRange();
31447             }
31448         } else {
31449             return this.doc.createRange();
31450         }
31451     },
31452     getParentElement: function()
31453     {
31454         
31455         this.assignDocWin();
31456         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31457         
31458         var range = this.createRange(sel);
31459          
31460         try {
31461             var p = range.commonAncestorContainer;
31462             while (p.nodeType == 3) { // text node
31463                 p = p.parentNode;
31464             }
31465             return p;
31466         } catch (e) {
31467             return null;
31468         }
31469     
31470     },
31471     /***
31472      *
31473      * Range intersection.. the hard stuff...
31474      *  '-1' = before
31475      *  '0' = hits..
31476      *  '1' = after.
31477      *         [ -- selected range --- ]
31478      *   [fail]                        [fail]
31479      *
31480      *    basically..
31481      *      if end is before start or  hits it. fail.
31482      *      if start is after end or hits it fail.
31483      *
31484      *   if either hits (but other is outside. - then it's not 
31485      *   
31486      *    
31487      **/
31488     
31489     
31490     // @see http://www.thismuchiknow.co.uk/?p=64.
31491     rangeIntersectsNode : function(range, node)
31492     {
31493         var nodeRange = node.ownerDocument.createRange();
31494         try {
31495             nodeRange.selectNode(node);
31496         } catch (e) {
31497             nodeRange.selectNodeContents(node);
31498         }
31499     
31500         var rangeStartRange = range.cloneRange();
31501         rangeStartRange.collapse(true);
31502     
31503         var rangeEndRange = range.cloneRange();
31504         rangeEndRange.collapse(false);
31505     
31506         var nodeStartRange = nodeRange.cloneRange();
31507         nodeStartRange.collapse(true);
31508     
31509         var nodeEndRange = nodeRange.cloneRange();
31510         nodeEndRange.collapse(false);
31511     
31512         return rangeStartRange.compareBoundaryPoints(
31513                  Range.START_TO_START, nodeEndRange) == -1 &&
31514                rangeEndRange.compareBoundaryPoints(
31515                  Range.START_TO_START, nodeStartRange) == 1;
31516         
31517          
31518     },
31519     rangeCompareNode : function(range, node)
31520     {
31521         var nodeRange = node.ownerDocument.createRange();
31522         try {
31523             nodeRange.selectNode(node);
31524         } catch (e) {
31525             nodeRange.selectNodeContents(node);
31526         }
31527         
31528         
31529         range.collapse(true);
31530     
31531         nodeRange.collapse(true);
31532      
31533         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31534         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
31535          
31536         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31537         
31538         var nodeIsBefore   =  ss == 1;
31539         var nodeIsAfter    = ee == -1;
31540         
31541         if (nodeIsBefore && nodeIsAfter) {
31542             return 0; // outer
31543         }
31544         if (!nodeIsBefore && nodeIsAfter) {
31545             return 1; //right trailed.
31546         }
31547         
31548         if (nodeIsBefore && !nodeIsAfter) {
31549             return 2;  // left trailed.
31550         }
31551         // fully contined.
31552         return 3;
31553     },
31554  
31555     cleanWordChars : function(input) {// change the chars to hex code
31556         
31557        var swapCodes  = [ 
31558             [    8211, "&#8211;" ], 
31559             [    8212, "&#8212;" ], 
31560             [    8216,  "'" ],  
31561             [    8217, "'" ],  
31562             [    8220, '"' ],  
31563             [    8221, '"' ],  
31564             [    8226, "*" ],  
31565             [    8230, "..." ]
31566         ]; 
31567         var output = input;
31568         Roo.each(swapCodes, function(sw) { 
31569             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
31570             
31571             output = output.replace(swapper, sw[1]);
31572         });
31573         
31574         return output;
31575     },
31576     
31577      
31578     
31579         
31580     
31581     cleanUpChild : function (node)
31582     {
31583         
31584         new Roo.htmleditor.FilterComment({node : node});
31585         new Roo.htmleditor.FilterAttributes({
31586                 node : node,
31587                 attrib_black : this.ablack,
31588                 attrib_clean : this.aclean,
31589                 style_white : this.cwhite,
31590                 style_black : this.cblack
31591         });
31592         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
31593         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
31594          
31595         
31596     },
31597     
31598     /**
31599      * Clean up MS wordisms...
31600      * @deprecated - use filter directly
31601      */
31602     cleanWord : function(node)
31603     {
31604         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
31605         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
31606         
31607     },
31608    
31609     
31610     /**
31611
31612      * @deprecated - use filters
31613      */
31614     cleanTableWidths : function(node)
31615     {
31616         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
31617         
31618  
31619     },
31620     
31621      
31622         
31623     applyBlacklists : function()
31624     {
31625         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
31626         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
31627         
31628         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
31629         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
31630         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
31631         
31632         this.white = [];
31633         this.black = [];
31634         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
31635             if (b.indexOf(tag) > -1) {
31636                 return;
31637             }
31638             this.white.push(tag);
31639             
31640         }, this);
31641         
31642         Roo.each(w, function(tag) {
31643             if (b.indexOf(tag) > -1) {
31644                 return;
31645             }
31646             if (this.white.indexOf(tag) > -1) {
31647                 return;
31648             }
31649             this.white.push(tag);
31650             
31651         }, this);
31652         
31653         
31654         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
31655             if (w.indexOf(tag) > -1) {
31656                 return;
31657             }
31658             this.black.push(tag);
31659             
31660         }, this);
31661         
31662         Roo.each(b, function(tag) {
31663             if (w.indexOf(tag) > -1) {
31664                 return;
31665             }
31666             if (this.black.indexOf(tag) > -1) {
31667                 return;
31668             }
31669             this.black.push(tag);
31670             
31671         }, this);
31672         
31673         
31674         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
31675         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
31676         
31677         this.cwhite = [];
31678         this.cblack = [];
31679         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
31680             if (b.indexOf(tag) > -1) {
31681                 return;
31682             }
31683             this.cwhite.push(tag);
31684             
31685         }, this);
31686         
31687         Roo.each(w, function(tag) {
31688             if (b.indexOf(tag) > -1) {
31689                 return;
31690             }
31691             if (this.cwhite.indexOf(tag) > -1) {
31692                 return;
31693             }
31694             this.cwhite.push(tag);
31695             
31696         }, this);
31697         
31698         
31699         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
31700             if (w.indexOf(tag) > -1) {
31701                 return;
31702             }
31703             this.cblack.push(tag);
31704             
31705         }, this);
31706         
31707         Roo.each(b, function(tag) {
31708             if (w.indexOf(tag) > -1) {
31709                 return;
31710             }
31711             if (this.cblack.indexOf(tag) > -1) {
31712                 return;
31713             }
31714             this.cblack.push(tag);
31715             
31716         }, this);
31717     },
31718     
31719     setStylesheets : function(stylesheets)
31720     {
31721         if(typeof(stylesheets) == 'string'){
31722             Roo.get(this.iframe.contentDocument.head).createChild({
31723                 tag : 'link',
31724                 rel : 'stylesheet',
31725                 type : 'text/css',
31726                 href : stylesheets
31727             });
31728             
31729             return;
31730         }
31731         var _this = this;
31732      
31733         Roo.each(stylesheets, function(s) {
31734             if(!s.length){
31735                 return;
31736             }
31737             
31738             Roo.get(_this.iframe.contentDocument.head).createChild({
31739                 tag : 'link',
31740                 rel : 'stylesheet',
31741                 type : 'text/css',
31742                 href : s
31743             });
31744         });
31745
31746         
31747     },
31748     
31749     
31750     updateLanguage : function()
31751     {
31752         if (!this.iframe || !this.iframe.contentDocument) {
31753             return;
31754         }
31755         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
31756     },
31757     
31758     
31759     removeStylesheets : function()
31760     {
31761         var _this = this;
31762         
31763         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
31764             s.remove();
31765         });
31766     },
31767     
31768     setStyle : function(style)
31769     {
31770         Roo.get(this.iframe.contentDocument.head).createChild({
31771             tag : 'style',
31772             type : 'text/css',
31773             html : style
31774         });
31775
31776         return;
31777     }
31778     
31779     // hide stuff that is not compatible
31780     /**
31781      * @event blur
31782      * @hide
31783      */
31784     /**
31785      * @event change
31786      * @hide
31787      */
31788     /**
31789      * @event focus
31790      * @hide
31791      */
31792     /**
31793      * @event specialkey
31794      * @hide
31795      */
31796     /**
31797      * @cfg {String} fieldClass @hide
31798      */
31799     /**
31800      * @cfg {String} focusClass @hide
31801      */
31802     /**
31803      * @cfg {String} autoCreate @hide
31804      */
31805     /**
31806      * @cfg {String} inputType @hide
31807      */
31808     /**
31809      * @cfg {String} invalidClass @hide
31810      */
31811     /**
31812      * @cfg {String} invalidText @hide
31813      */
31814     /**
31815      * @cfg {String} msgFx @hide
31816      */
31817     /**
31818      * @cfg {String} validateOnBlur @hide
31819      */
31820 });
31821
31822 Roo.HtmlEditorCore.white = [
31823         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
31824         
31825        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
31826        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
31827        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
31828        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
31829        'TABLE',   'UL',         'XMP', 
31830        
31831        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
31832       'THEAD',   'TR', 
31833      
31834       'DIR', 'MENU', 'OL', 'UL', 'DL',
31835        
31836       'EMBED',  'OBJECT'
31837 ];
31838
31839
31840 Roo.HtmlEditorCore.black = [
31841     //    'embed',  'object', // enable - backend responsiblity to clean thiese
31842         'APPLET', // 
31843         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
31844         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
31845         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
31846         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
31847         //'FONT' // CLEAN LATER..
31848         'COLGROUP', 'COL'   // messy tables.
31849         
31850         
31851 ];
31852 Roo.HtmlEditorCore.clean = [ // ?? needed???
31853      'SCRIPT', 'STYLE', 'TITLE', 'XML'
31854 ];
31855 Roo.HtmlEditorCore.tag_remove = [
31856     'FONT', 'TBODY'  
31857 ];
31858 // attributes..
31859
31860 Roo.HtmlEditorCore.ablack = [
31861     'on'
31862 ];
31863     
31864 Roo.HtmlEditorCore.aclean = [ 
31865     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
31866 ];
31867
31868 // protocols..
31869 Roo.HtmlEditorCore.pwhite= [
31870         'http',  'https',  'mailto'
31871 ];
31872
31873 // white listed style attributes.
31874 Roo.HtmlEditorCore.cwhite= [
31875       //  'text-align', /// default is to allow most things..
31876       
31877          
31878 //        'font-size'//??
31879 ];
31880
31881 // black listed style attributes.
31882 Roo.HtmlEditorCore.cblack= [
31883       //  'font-size' -- this can be set by the project 
31884 ];
31885
31886
31887
31888
31889     /*
31890  * - LGPL
31891  *
31892  * HtmlEditor
31893  * 
31894  */
31895
31896 /**
31897  * @class Roo.bootstrap.form.HtmlEditor
31898  * @extends Roo.bootstrap.form.TextArea
31899  * Bootstrap HtmlEditor class
31900
31901  * @constructor
31902  * Create a new HtmlEditor
31903  * @param {Object} config The config object
31904  */
31905
31906 Roo.bootstrap.form.HtmlEditor = function(config){
31907     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
31908     if (!this.toolbars) {
31909         this.toolbars = [];
31910     }
31911     
31912     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
31913     this.addEvents({
31914             /**
31915              * @event initialize
31916              * Fires when the editor is fully initialized (including the iframe)
31917              * @param {HtmlEditor} this
31918              */
31919             initialize: true,
31920             /**
31921              * @event activate
31922              * Fires when the editor is first receives the focus. Any insertion must wait
31923              * until after this event.
31924              * @param {HtmlEditor} this
31925              */
31926             activate: true,
31927              /**
31928              * @event beforesync
31929              * Fires before the textarea is updated with content from the editor iframe. Return false
31930              * to cancel the sync.
31931              * @param {HtmlEditor} this
31932              * @param {String} html
31933              */
31934             beforesync: true,
31935              /**
31936              * @event beforepush
31937              * Fires before the iframe editor is updated with content from the textarea. Return false
31938              * to cancel the push.
31939              * @param {HtmlEditor} this
31940              * @param {String} html
31941              */
31942             beforepush: true,
31943              /**
31944              * @event sync
31945              * Fires when the textarea is updated with content from the editor iframe.
31946              * @param {HtmlEditor} this
31947              * @param {String} html
31948              */
31949             sync: true,
31950              /**
31951              * @event push
31952              * Fires when the iframe editor is updated with content from the textarea.
31953              * @param {HtmlEditor} this
31954              * @param {String} html
31955              */
31956             push: true,
31957              /**
31958              * @event editmodechange
31959              * Fires when the editor switches edit modes
31960              * @param {HtmlEditor} this
31961              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
31962              */
31963             editmodechange: true,
31964             /**
31965              * @event editorevent
31966              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31967              * @param {HtmlEditor} this
31968              */
31969             editorevent: true,
31970             /**
31971              * @event firstfocus
31972              * Fires when on first focus - needed by toolbars..
31973              * @param {HtmlEditor} this
31974              */
31975             firstfocus: true,
31976             /**
31977              * @event autosave
31978              * Auto save the htmlEditor value as a file into Events
31979              * @param {HtmlEditor} this
31980              */
31981             autosave: true,
31982             /**
31983              * @event savedpreview
31984              * preview the saved version of htmlEditor
31985              * @param {HtmlEditor} this
31986              */
31987             savedpreview: true
31988         });
31989 };
31990
31991
31992 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
31993     
31994     
31995       /**
31996      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
31997      */
31998     toolbars : false,
31999     
32000      /**
32001     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32002     */
32003     btns : [],
32004    
32005      /**
32006      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
32007      *                        Roo.resizable.
32008      */
32009     resizable : false,
32010      /**
32011      * @cfg {Number} height (in pixels)
32012      */   
32013     height: 300,
32014    /**
32015      * @cfg {Number} width (in pixels)
32016      */   
32017     width: false,
32018     
32019     /**
32020      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32021      * 
32022      */
32023     stylesheets: false,
32024     
32025     // id of frame..
32026     frameId: false,
32027     
32028     // private properties
32029     validationEvent : false,
32030     deferHeight: true,
32031     initialized : false,
32032     activated : false,
32033     
32034     onFocus : Roo.emptyFn,
32035     iframePad:3,
32036     hideMode:'offsets',
32037     
32038     tbContainer : false,
32039     
32040     bodyCls : '',
32041     
32042     toolbarContainer :function() {
32043         return this.wrap.select('.x-html-editor-tb',true).first();
32044     },
32045
32046     /**
32047      * Protected method that will not generally be called directly. It
32048      * is called when the editor creates its toolbar. Override this method if you need to
32049      * add custom toolbar buttons.
32050      * @param {HtmlEditor} editor
32051      */
32052     createToolbar : function(){
32053         Roo.log('renewing');
32054         Roo.log("create toolbars");
32055         
32056         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32057         this.toolbars[0].render(this.toolbarContainer());
32058         
32059         return;
32060         
32061 //        if (!editor.toolbars || !editor.toolbars.length) {
32062 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32063 //        }
32064 //        
32065 //        for (var i =0 ; i < editor.toolbars.length;i++) {
32066 //            editor.toolbars[i] = Roo.factory(
32067 //                    typeof(editor.toolbars[i]) == 'string' ?
32068 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
32069 //                Roo.bootstrap.form.HtmlEditor);
32070 //            editor.toolbars[i].init(editor);
32071 //        }
32072     },
32073
32074      
32075     // private
32076     onRender : function(ct, position)
32077     {
32078        // Roo.log("Call onRender: " + this.xtype);
32079         var _t = this;
32080         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32081       
32082         this.wrap = this.inputEl().wrap({
32083             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32084         });
32085         
32086         this.editorcore.onRender(ct, position);
32087          
32088         if (this.resizable) {
32089             this.resizeEl = new Roo.Resizable(this.wrap, {
32090                 pinned : true,
32091                 wrap: true,
32092                 dynamic : true,
32093                 minHeight : this.height,
32094                 height: this.height,
32095                 handles : this.resizable,
32096                 width: this.width,
32097                 listeners : {
32098                     resize : function(r, w, h) {
32099                         _t.onResize(w,h); // -something
32100                     }
32101                 }
32102             });
32103             
32104         }
32105         this.createToolbar(this);
32106        
32107         
32108         if(!this.width && this.resizable){
32109             this.setSize(this.wrap.getSize());
32110         }
32111         if (this.resizeEl) {
32112             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32113             // should trigger onReize..
32114         }
32115         
32116     },
32117
32118     // private
32119     onResize : function(w, h)
32120     {
32121         Roo.log('resize: ' +w + ',' + h );
32122         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32123         var ew = false;
32124         var eh = false;
32125         
32126         if(this.inputEl() ){
32127             if(typeof w == 'number'){
32128                 var aw = w - this.wrap.getFrameWidth('lr');
32129                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32130                 ew = aw;
32131             }
32132             if(typeof h == 'number'){
32133                  var tbh = -11;  // fixme it needs to tool bar size!
32134                 for (var i =0; i < this.toolbars.length;i++) {
32135                     // fixme - ask toolbars for heights?
32136                     tbh += this.toolbars[i].el.getHeight();
32137                     //if (this.toolbars[i].footer) {
32138                     //    tbh += this.toolbars[i].footer.el.getHeight();
32139                     //}
32140                 }
32141               
32142                 
32143                 
32144                 
32145                 
32146                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32147                 ah -= 5; // knock a few pixes off for look..
32148                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32149                 var eh = ah;
32150             }
32151         }
32152         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32153         this.editorcore.onResize(ew,eh);
32154         
32155     },
32156
32157     /**
32158      * Toggles the editor between standard and source edit mode.
32159      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32160      */
32161     toggleSourceEdit : function(sourceEditMode)
32162     {
32163         this.editorcore.toggleSourceEdit(sourceEditMode);
32164         
32165         if(this.editorcore.sourceEditMode){
32166             Roo.log('editor - showing textarea');
32167             
32168 //            Roo.log('in');
32169 //            Roo.log(this.syncValue());
32170             this.syncValue();
32171             this.inputEl().removeClass(['hide', 'x-hidden']);
32172             this.inputEl().dom.removeAttribute('tabIndex');
32173             this.inputEl().focus();
32174         }else{
32175             Roo.log('editor - hiding textarea');
32176 //            Roo.log('out')
32177 //            Roo.log(this.pushValue()); 
32178             this.pushValue();
32179             
32180             this.inputEl().addClass(['hide', 'x-hidden']);
32181             this.inputEl().dom.setAttribute('tabIndex', -1);
32182             //this.deferFocus();
32183         }
32184          
32185         if(this.resizable){
32186             this.setSize(this.wrap.getSize());
32187         }
32188         
32189         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32190     },
32191  
32192     // private (for BoxComponent)
32193     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32194
32195     // private (for BoxComponent)
32196     getResizeEl : function(){
32197         return this.wrap;
32198     },
32199
32200     // private (for BoxComponent)
32201     getPositionEl : function(){
32202         return this.wrap;
32203     },
32204
32205     // private
32206     initEvents : function(){
32207         this.originalValue = this.getValue();
32208     },
32209
32210 //    /**
32211 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32212 //     * @method
32213 //     */
32214 //    markInvalid : Roo.emptyFn,
32215 //    /**
32216 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32217 //     * @method
32218 //     */
32219 //    clearInvalid : Roo.emptyFn,
32220
32221     setValue : function(v){
32222         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32223         this.editorcore.pushValue();
32224     },
32225
32226      
32227     // private
32228     deferFocus : function(){
32229         this.focus.defer(10, this);
32230     },
32231
32232     // doc'ed in Field
32233     focus : function(){
32234         this.editorcore.focus();
32235         
32236     },
32237       
32238
32239     // private
32240     onDestroy : function(){
32241         
32242         
32243         
32244         if(this.rendered){
32245             
32246             for (var i =0; i < this.toolbars.length;i++) {
32247                 // fixme - ask toolbars for heights?
32248                 this.toolbars[i].onDestroy();
32249             }
32250             
32251             this.wrap.dom.innerHTML = '';
32252             this.wrap.remove();
32253         }
32254     },
32255
32256     // private
32257     onFirstFocus : function(){
32258         //Roo.log("onFirstFocus");
32259         this.editorcore.onFirstFocus();
32260          for (var i =0; i < this.toolbars.length;i++) {
32261             this.toolbars[i].onFirstFocus();
32262         }
32263         
32264     },
32265     
32266     // private
32267     syncValue : function()
32268     {   
32269         this.editorcore.syncValue();
32270     },
32271     
32272     pushValue : function()
32273     {   
32274         this.editorcore.pushValue();
32275     }
32276      
32277     
32278     // hide stuff that is not compatible
32279     /**
32280      * @event blur
32281      * @hide
32282      */
32283     /**
32284      * @event change
32285      * @hide
32286      */
32287     /**
32288      * @event focus
32289      * @hide
32290      */
32291     /**
32292      * @event specialkey
32293      * @hide
32294      */
32295     /**
32296      * @cfg {String} fieldClass @hide
32297      */
32298     /**
32299      * @cfg {String} focusClass @hide
32300      */
32301     /**
32302      * @cfg {String} autoCreate @hide
32303      */
32304     /**
32305      * @cfg {String} inputType @hide
32306      */
32307      
32308     /**
32309      * @cfg {String} invalidText @hide
32310      */
32311     /**
32312      * @cfg {String} msgFx @hide
32313      */
32314     /**
32315      * @cfg {String} validateOnBlur @hide
32316      */
32317 });
32318  
32319     
32320    
32321    
32322    
32323       
32324 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32325 /**
32326  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32327  * @parent Roo.bootstrap.form.HtmlEditor
32328  * @extends Roo.bootstrap.nav.Simplebar
32329  * Basic Toolbar
32330  * 
32331  * @example
32332  * Usage:
32333  *
32334  new Roo.bootstrap.form.HtmlEditor({
32335     ....
32336     toolbars : [
32337         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32338             disable : { fonts: 1 , format: 1, ..., ... , ...],
32339             btns : [ .... ]
32340         })
32341     }
32342      
32343  * 
32344  * @cfg {Object} disable List of elements to disable..
32345  * @cfg {Array} btns List of additional buttons.
32346  * 
32347  * 
32348  * NEEDS Extra CSS? 
32349  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32350  */
32351  
32352 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32353 {
32354     
32355     Roo.apply(this, config);
32356     
32357     // default disabled, based on 'good practice'..
32358     this.disable = this.disable || {};
32359     Roo.applyIf(this.disable, {
32360         fontSize : true,
32361         colors : true,
32362         specialElements : true
32363     });
32364     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32365     
32366     this.editor = config.editor;
32367     this.editorcore = config.editor.editorcore;
32368     
32369     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32370     
32371     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32372     // dont call parent... till later.
32373 }
32374 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32375      
32376     bar : true,
32377     
32378     editor : false,
32379     editorcore : false,
32380     
32381     
32382     formats : [
32383         "p" ,  
32384         "h1","h2","h3","h4","h5","h6", 
32385         "pre", "code", 
32386         "abbr", "acronym", "address", "cite", "samp", "var",
32387         'div','span'
32388     ],
32389     
32390     onRender : function(ct, position)
32391     {
32392        // Roo.log("Call onRender: " + this.xtype);
32393         
32394        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32395        Roo.log(this.el);
32396        this.el.dom.style.marginBottom = '0';
32397        var _this = this;
32398        var editorcore = this.editorcore;
32399        var editor= this.editor;
32400        
32401        var children = [];
32402        var btn = function(id,cmd , toggle, handler, html){
32403        
32404             var  event = toggle ? 'toggle' : 'click';
32405        
32406             var a = {
32407                 size : 'sm',
32408                 xtype: 'Button',
32409                 xns: Roo.bootstrap,
32410                 //glyphicon : id,
32411                 fa: id,
32412                 cmd : id || cmd,
32413                 enableToggle:toggle !== false,
32414                 html : html || '',
32415                 pressed : toggle ? false : null,
32416                 listeners : {}
32417             };
32418             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32419                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32420             };
32421             children.push(a);
32422             return a;
32423        }
32424        
32425     //    var cb_box = function...
32426         
32427         var style = {
32428                 xtype: 'Button',
32429                 size : 'sm',
32430                 xns: Roo.bootstrap,
32431                 fa : 'font',
32432                 //html : 'submit'
32433                 menu : {
32434                     xtype: 'Menu',
32435                     xns: Roo.bootstrap,
32436                     items:  []
32437                 }
32438         };
32439         Roo.each(this.formats, function(f) {
32440             style.menu.items.push({
32441                 xtype :'MenuItem',
32442                 xns: Roo.bootstrap,
32443                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32444                 tagname : f,
32445                 listeners : {
32446                     click : function()
32447                     {
32448                         editorcore.insertTag(this.tagname);
32449                         editor.focus();
32450                     }
32451                 }
32452                 
32453             });
32454         });
32455         children.push(style);   
32456         
32457         btn('bold',false,true);
32458         btn('italic',false,true);
32459         btn('align-left', 'justifyleft',true);
32460         btn('align-center', 'justifycenter',true);
32461         btn('align-right' , 'justifyright',true);
32462         btn('link', false, false, function(btn) {
32463             //Roo.log("create link?");
32464             var url = prompt(this.createLinkText, this.defaultLinkValue);
32465             if(url && url != 'http:/'+'/'){
32466                 this.editorcore.relayCmd('createlink', url);
32467             }
32468         }),
32469         btn('list','insertunorderedlist',true);
32470         btn('pencil', false,true, function(btn){
32471                 Roo.log(this);
32472                 this.toggleSourceEdit(btn.pressed);
32473         });
32474         
32475         if (this.editor.btns.length > 0) {
32476             for (var i = 0; i<this.editor.btns.length; i++) {
32477                 children.push(this.editor.btns[i]);
32478             }
32479         }
32480         
32481         /*
32482         var cog = {
32483                 xtype: 'Button',
32484                 size : 'sm',
32485                 xns: Roo.bootstrap,
32486                 glyphicon : 'cog',
32487                 //html : 'submit'
32488                 menu : {
32489                     xtype: 'Menu',
32490                     xns: Roo.bootstrap,
32491                     items:  []
32492                 }
32493         };
32494         
32495         cog.menu.items.push({
32496             xtype :'MenuItem',
32497             xns: Roo.bootstrap,
32498             html : Clean styles,
32499             tagname : f,
32500             listeners : {
32501                 click : function()
32502                 {
32503                     editorcore.insertTag(this.tagname);
32504                     editor.focus();
32505                 }
32506             }
32507             
32508         });
32509        */
32510         
32511          
32512        this.xtype = 'NavSimplebar';
32513         
32514         for(var i=0;i< children.length;i++) {
32515             
32516             this.buttons.add(this.addxtypeChild(children[i]));
32517             
32518         }
32519         
32520         editor.on('editorevent', this.updateToolbar, this);
32521     },
32522     onBtnClick : function(id)
32523     {
32524        this.editorcore.relayCmd(id);
32525        this.editorcore.focus();
32526     },
32527     
32528     /**
32529      * Protected method that will not generally be called directly. It triggers
32530      * a toolbar update by reading the markup state of the current selection in the editor.
32531      */
32532     updateToolbar: function(){
32533
32534         if(!this.editorcore.activated){
32535             this.editor.onFirstFocus(); // is this neeed?
32536             return;
32537         }
32538
32539         var btns = this.buttons; 
32540         var doc = this.editorcore.doc;
32541         btns.get('bold').setActive(doc.queryCommandState('bold'));
32542         btns.get('italic').setActive(doc.queryCommandState('italic'));
32543         //btns.get('underline').setActive(doc.queryCommandState('underline'));
32544         
32545         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32546         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32547         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32548         
32549         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32550         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32551          /*
32552         
32553         var ans = this.editorcore.getAllAncestors();
32554         if (this.formatCombo) {
32555             
32556             
32557             var store = this.formatCombo.store;
32558             this.formatCombo.setValue("");
32559             for (var i =0; i < ans.length;i++) {
32560                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32561                     // select it..
32562                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
32563                     break;
32564                 }
32565             }
32566         }
32567         
32568         
32569         
32570         // hides menus... - so this cant be on a menu...
32571         Roo.bootstrap.MenuMgr.hideAll();
32572         */
32573         Roo.bootstrap.menu.Manager.hideAll();
32574         //this.editorsyncValue();
32575     },
32576     onFirstFocus: function() {
32577         this.buttons.each(function(item){
32578            item.enable();
32579         });
32580     },
32581     toggleSourceEdit : function(sourceEditMode){
32582         
32583           
32584         if(sourceEditMode){
32585             Roo.log("disabling buttons");
32586            this.buttons.each( function(item){
32587                 if(item.cmd != 'pencil'){
32588                     item.disable();
32589                 }
32590             });
32591           
32592         }else{
32593             Roo.log("enabling buttons");
32594             if(this.editorcore.initialized){
32595                 this.buttons.each( function(item){
32596                     item.enable();
32597                 });
32598             }
32599             
32600         }
32601         Roo.log("calling toggole on editor");
32602         // tell the editor that it's been pressed..
32603         this.editor.toggleSourceEdit(sourceEditMode);
32604        
32605     }
32606 });
32607
32608
32609
32610
32611  
32612 /*
32613  * - LGPL
32614  */
32615
32616 /**
32617  * @class Roo.bootstrap.form.Markdown
32618  * @extends Roo.bootstrap.form.TextArea
32619  * Bootstrap Showdown editable area
32620  * @cfg {string} content
32621  * 
32622  * @constructor
32623  * Create a new Showdown
32624  */
32625
32626 Roo.bootstrap.form.Markdown = function(config){
32627     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
32628    
32629 };
32630
32631 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
32632     
32633     editing :false,
32634     
32635     initEvents : function()
32636     {
32637         
32638         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
32639         this.markdownEl = this.el.createChild({
32640             cls : 'roo-markdown-area'
32641         });
32642         this.inputEl().addClass('d-none');
32643         if (this.getValue() == '') {
32644             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32645             
32646         } else {
32647             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32648         }
32649         this.markdownEl.on('click', this.toggleTextEdit, this);
32650         this.on('blur', this.toggleTextEdit, this);
32651         this.on('specialkey', this.resizeTextArea, this);
32652     },
32653     
32654     toggleTextEdit : function()
32655     {
32656         var sh = this.markdownEl.getHeight();
32657         this.inputEl().addClass('d-none');
32658         this.markdownEl.addClass('d-none');
32659         if (!this.editing) {
32660             // show editor?
32661             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
32662             this.inputEl().removeClass('d-none');
32663             this.inputEl().focus();
32664             this.editing = true;
32665             return;
32666         }
32667         // show showdown...
32668         this.updateMarkdown();
32669         this.markdownEl.removeClass('d-none');
32670         this.editing = false;
32671         return;
32672     },
32673     updateMarkdown : function()
32674     {
32675         if (this.getValue() == '') {
32676             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32677             return;
32678         }
32679  
32680         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32681     },
32682     
32683     resizeTextArea: function () {
32684         
32685         var sh = 100;
32686         Roo.log([sh, this.getValue().split("\n").length * 30]);
32687         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
32688     },
32689     setValue : function(val)
32690     {
32691         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
32692         if (!this.editing) {
32693             this.updateMarkdown();
32694         }
32695         
32696     },
32697     focus : function()
32698     {
32699         if (!this.editing) {
32700             this.toggleTextEdit();
32701         }
32702         
32703     }
32704
32705
32706 });/*
32707  * Based on:
32708  * Ext JS Library 1.1.1
32709  * Copyright(c) 2006-2007, Ext JS, LLC.
32710  *
32711  * Originally Released Under LGPL - original licence link has changed is not relivant.
32712  *
32713  * Fork - LGPL
32714  * <script type="text/javascript">
32715  */
32716  
32717 /**
32718  * @class Roo.bootstrap.PagingToolbar
32719  * @extends Roo.bootstrap.nav.Simplebar
32720  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32721  * @constructor
32722  * Create a new PagingToolbar
32723  * @param {Object} config The config object
32724  * @param {Roo.data.Store} store
32725  */
32726 Roo.bootstrap.PagingToolbar = function(config)
32727 {
32728     // old args format still supported... - xtype is prefered..
32729         // created from xtype...
32730     
32731     this.ds = config.dataSource;
32732     
32733     if (config.store && !this.ds) {
32734         this.store= Roo.factory(config.store, Roo.data);
32735         this.ds = this.store;
32736         this.ds.xmodule = this.xmodule || false;
32737     }
32738     
32739     this.toolbarItems = [];
32740     if (config.items) {
32741         this.toolbarItems = config.items;
32742     }
32743     
32744     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
32745     
32746     this.cursor = 0;
32747     
32748     if (this.ds) { 
32749         this.bind(this.ds);
32750     }
32751     
32752     if (Roo.bootstrap.version == 4) {
32753         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
32754     } else {
32755         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
32756     }
32757     
32758 };
32759
32760 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
32761     /**
32762      * @cfg {Roo.bootstrap.Button} buttons[]
32763      * Buttons for the toolbar
32764      */
32765      /**
32766      * @cfg {Roo.data.Store} store
32767      * The underlying data store providing the paged data
32768      */
32769     /**
32770      * @cfg {String/HTMLElement/Element} container
32771      * container The id or element that will contain the toolbar
32772      */
32773     /**
32774      * @cfg {Boolean} displayInfo
32775      * True to display the displayMsg (defaults to false)
32776      */
32777     /**
32778      * @cfg {Number} pageSize
32779      * The number of records to display per page (defaults to 20)
32780      */
32781     pageSize: 20,
32782     /**
32783      * @cfg {String} displayMsg
32784      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32785      */
32786     displayMsg : 'Displaying {0} - {1} of {2}',
32787     /**
32788      * @cfg {String} emptyMsg
32789      * The message to display when no records are found (defaults to "No data to display")
32790      */
32791     emptyMsg : 'No data to display',
32792     /**
32793      * Customizable piece of the default paging text (defaults to "Page")
32794      * @type String
32795      */
32796     beforePageText : "Page",
32797     /**
32798      * Customizable piece of the default paging text (defaults to "of %0")
32799      * @type String
32800      */
32801     afterPageText : "of {0}",
32802     /**
32803      * Customizable piece of the default paging text (defaults to "First Page")
32804      * @type String
32805      */
32806     firstText : "First Page",
32807     /**
32808      * Customizable piece of the default paging text (defaults to "Previous Page")
32809      * @type String
32810      */
32811     prevText : "Previous Page",
32812     /**
32813      * Customizable piece of the default paging text (defaults to "Next Page")
32814      * @type String
32815      */
32816     nextText : "Next Page",
32817     /**
32818      * Customizable piece of the default paging text (defaults to "Last Page")
32819      * @type String
32820      */
32821     lastText : "Last Page",
32822     /**
32823      * Customizable piece of the default paging text (defaults to "Refresh")
32824      * @type String
32825      */
32826     refreshText : "Refresh",
32827
32828     buttons : false,
32829     // private
32830     onRender : function(ct, position) 
32831     {
32832         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
32833         this.navgroup.parentId = this.id;
32834         this.navgroup.onRender(this.el, null);
32835         // add the buttons to the navgroup
32836         
32837         if(this.displayInfo){
32838             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
32839             this.displayEl = this.el.select('.x-paging-info', true).first();
32840 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
32841 //            this.displayEl = navel.el.select('span',true).first();
32842         }
32843         
32844         var _this = this;
32845         
32846         if(this.buttons){
32847             Roo.each(_this.buttons, function(e){ // this might need to use render????
32848                Roo.factory(e).render(_this.el);
32849             });
32850         }
32851             
32852         Roo.each(_this.toolbarItems, function(e) {
32853             _this.navgroup.addItem(e);
32854         });
32855         
32856         
32857         this.first = this.navgroup.addItem({
32858             tooltip: this.firstText,
32859             cls: "prev btn-outline-secondary",
32860             html : ' <i class="fa fa-step-backward"></i>',
32861             disabled: true,
32862             preventDefault: true,
32863             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
32864         });
32865         
32866         this.prev =  this.navgroup.addItem({
32867             tooltip: this.prevText,
32868             cls: "prev btn-outline-secondary",
32869             html : ' <i class="fa fa-backward"></i>',
32870             disabled: true,
32871             preventDefault: true,
32872             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
32873         });
32874     //this.addSeparator();
32875         
32876         
32877         var field = this.navgroup.addItem( {
32878             tagtype : 'span',
32879             cls : 'x-paging-position  btn-outline-secondary',
32880              disabled: true,
32881             html : this.beforePageText  +
32882                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
32883                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
32884          } ); //?? escaped?
32885         
32886         this.field = field.el.select('input', true).first();
32887         this.field.on("keydown", this.onPagingKeydown, this);
32888         this.field.on("focus", function(){this.dom.select();});
32889     
32890     
32891         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
32892         //this.field.setHeight(18);
32893         //this.addSeparator();
32894         this.next = this.navgroup.addItem({
32895             tooltip: this.nextText,
32896             cls: "next btn-outline-secondary",
32897             html : ' <i class="fa fa-forward"></i>',
32898             disabled: true,
32899             preventDefault: true,
32900             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
32901         });
32902         this.last = this.navgroup.addItem({
32903             tooltip: this.lastText,
32904             html : ' <i class="fa fa-step-forward"></i>',
32905             cls: "next btn-outline-secondary",
32906             disabled: true,
32907             preventDefault: true,
32908             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
32909         });
32910     //this.addSeparator();
32911         this.loading = this.navgroup.addItem({
32912             tooltip: this.refreshText,
32913             cls: "btn-outline-secondary",
32914             html : ' <i class="fa fa-refresh"></i>',
32915             preventDefault: true,
32916             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
32917         });
32918         
32919     },
32920
32921     // private
32922     updateInfo : function(){
32923         if(this.displayEl){
32924             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
32925             var msg = count == 0 ?
32926                 this.emptyMsg :
32927                 String.format(
32928                     this.displayMsg,
32929                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32930                 );
32931             this.displayEl.update(msg);
32932         }
32933     },
32934
32935     // private
32936     onLoad : function(ds, r, o)
32937     {
32938         this.cursor = o.params && o.params.start ? o.params.start : 0;
32939         
32940         var d = this.getPageData(),
32941             ap = d.activePage,
32942             ps = d.pages;
32943         
32944         
32945         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
32946         this.field.dom.value = ap;
32947         this.first.setDisabled(ap == 1);
32948         this.prev.setDisabled(ap == 1);
32949         this.next.setDisabled(ap == ps);
32950         this.last.setDisabled(ap == ps);
32951         this.loading.enable();
32952         this.updateInfo();
32953     },
32954
32955     // private
32956     getPageData : function(){
32957         var total = this.ds.getTotalCount();
32958         return {
32959             total : total,
32960             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32961             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32962         };
32963     },
32964
32965     // private
32966     onLoadError : function(proxy, o){
32967         this.loading.enable();
32968         if (this.ds.events.loadexception.listeners.length  < 2) {
32969             // nothing has been assigned to loadexception except this...
32970             // so 
32971             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
32972
32973         }
32974     },
32975
32976     // private
32977     onPagingKeydown : function(e){
32978         var k = e.getKey();
32979         var d = this.getPageData();
32980         if(k == e.RETURN){
32981             var v = this.field.dom.value, pageNum;
32982             if(!v || isNaN(pageNum = parseInt(v, 10))){
32983                 this.field.dom.value = d.activePage;
32984                 return;
32985             }
32986             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32987             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32988             e.stopEvent();
32989         }
32990         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))
32991         {
32992           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32993           this.field.dom.value = pageNum;
32994           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32995           e.stopEvent();
32996         }
32997         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32998         {
32999           var v = this.field.dom.value, pageNum; 
33000           var increment = (e.shiftKey) ? 10 : 1;
33001           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33002                 increment *= -1;
33003           }
33004           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33005             this.field.dom.value = d.activePage;
33006             return;
33007           }
33008           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33009           {
33010             this.field.dom.value = parseInt(v, 10) + increment;
33011             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33012             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33013           }
33014           e.stopEvent();
33015         }
33016     },
33017
33018     // private
33019     beforeLoad : function(){
33020         if(this.loading){
33021             this.loading.disable();
33022         }
33023     },
33024
33025     // private
33026     onClick : function(which){
33027         
33028         var ds = this.ds;
33029         if (!ds) {
33030             return;
33031         }
33032         
33033         switch(which){
33034             case "first":
33035                 ds.load({params:{start: 0, limit: this.pageSize}});
33036             break;
33037             case "prev":
33038                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33039             break;
33040             case "next":
33041                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33042             break;
33043             case "last":
33044                 var total = ds.getTotalCount();
33045                 var extra = total % this.pageSize;
33046                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33047                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33048             break;
33049             case "refresh":
33050                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33051             break;
33052         }
33053     },
33054
33055     /**
33056      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33057      * @param {Roo.data.Store} store The data store to unbind
33058      */
33059     unbind : function(ds){
33060         ds.un("beforeload", this.beforeLoad, this);
33061         ds.un("load", this.onLoad, this);
33062         ds.un("loadexception", this.onLoadError, this);
33063         ds.un("remove", this.updateInfo, this);
33064         ds.un("add", this.updateInfo, this);
33065         this.ds = undefined;
33066     },
33067
33068     /**
33069      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33070      * @param {Roo.data.Store} store The data store to bind
33071      */
33072     bind : function(ds){
33073         ds.on("beforeload", this.beforeLoad, this);
33074         ds.on("load", this.onLoad, this);
33075         ds.on("loadexception", this.onLoadError, this);
33076         ds.on("remove", this.updateInfo, this);
33077         ds.on("add", this.updateInfo, this);
33078         this.ds = ds;
33079     }
33080 });/*
33081  * - LGPL
33082  *
33083  * element
33084  * 
33085  */
33086
33087 /**
33088  * @class Roo.bootstrap.MessageBar
33089  * @extends Roo.bootstrap.Component
33090  * Bootstrap MessageBar class
33091  * @cfg {String} html contents of the MessageBar
33092  * @cfg {String} weight (info | success | warning | danger) default info
33093  * @cfg {String} beforeClass insert the bar before the given class
33094  * @cfg {Boolean} closable (true | false) default false
33095  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33096  * 
33097  * @constructor
33098  * Create a new Element
33099  * @param {Object} config The config object
33100  */
33101
33102 Roo.bootstrap.MessageBar = function(config){
33103     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33104 };
33105
33106 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33107     
33108     html: '',
33109     weight: 'info',
33110     closable: false,
33111     fixed: false,
33112     beforeClass: 'bootstrap-sticky-wrap',
33113     
33114     getAutoCreate : function(){
33115         
33116         var cfg = {
33117             tag: 'div',
33118             cls: 'alert alert-dismissable alert-' + this.weight,
33119             cn: [
33120                 {
33121                     tag: 'span',
33122                     cls: 'message',
33123                     html: this.html || ''
33124                 }
33125             ]
33126         };
33127         
33128         if(this.fixed){
33129             cfg.cls += ' alert-messages-fixed';
33130         }
33131         
33132         if(this.closable){
33133             cfg.cn.push({
33134                 tag: 'button',
33135                 cls: 'close',
33136                 html: 'x'
33137             });
33138         }
33139         
33140         return cfg;
33141     },
33142     
33143     onRender : function(ct, position)
33144     {
33145         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33146         
33147         if(!this.el){
33148             var cfg = Roo.apply({},  this.getAutoCreate());
33149             cfg.id = Roo.id();
33150             
33151             if (this.cls) {
33152                 cfg.cls += ' ' + this.cls;
33153             }
33154             if (this.style) {
33155                 cfg.style = this.style;
33156             }
33157             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33158             
33159             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33160         }
33161         
33162         this.el.select('>button.close').on('click', this.hide, this);
33163         
33164     },
33165     
33166     show : function()
33167     {
33168         if (!this.rendered) {
33169             this.render();
33170         }
33171         
33172         this.el.show();
33173         
33174         this.fireEvent('show', this);
33175         
33176     },
33177     
33178     hide : function()
33179     {
33180         if (!this.rendered) {
33181             this.render();
33182         }
33183         
33184         this.el.hide();
33185         
33186         this.fireEvent('hide', this);
33187     },
33188     
33189     update : function()
33190     {
33191 //        var e = this.el.dom.firstChild;
33192 //        
33193 //        if(this.closable){
33194 //            e = e.nextSibling;
33195 //        }
33196 //        
33197 //        e.data = this.html || '';
33198
33199         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33200     }
33201    
33202 });
33203
33204  
33205
33206      /*
33207  * - LGPL
33208  *
33209  * Graph
33210  * 
33211  */
33212
33213
33214 /**
33215  * @class Roo.bootstrap.Graph
33216  * @extends Roo.bootstrap.Component
33217  * Bootstrap Graph class
33218 > Prameters
33219  -sm {number} sm 4
33220  -md {number} md 5
33221  @cfg {String} graphtype  bar | vbar | pie
33222  @cfg {number} g_x coodinator | centre x (pie)
33223  @cfg {number} g_y coodinator | centre y (pie)
33224  @cfg {number} g_r radius (pie)
33225  @cfg {number} g_height height of the chart (respected by all elements in the set)
33226  @cfg {number} g_width width of the chart (respected by all elements in the set)
33227  @cfg {Object} title The title of the chart
33228     
33229  -{Array}  values
33230  -opts (object) options for the chart 
33231      o {
33232      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33233      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33234      o vgutter (number)
33235      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.
33236      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33237      o to
33238      o stretch (boolean)
33239      o }
33240  -opts (object) options for the pie
33241      o{
33242      o cut
33243      o startAngle (number)
33244      o endAngle (number)
33245      } 
33246  *
33247  * @constructor
33248  * Create a new Input
33249  * @param {Object} config The config object
33250  */
33251
33252 Roo.bootstrap.Graph = function(config){
33253     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33254     
33255     this.addEvents({
33256         // img events
33257         /**
33258          * @event click
33259          * The img click event for the img.
33260          * @param {Roo.EventObject} e
33261          */
33262         "click" : true
33263     });
33264 };
33265
33266 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33267     
33268     sm: 4,
33269     md: 5,
33270     graphtype: 'bar',
33271     g_height: 250,
33272     g_width: 400,
33273     g_x: 50,
33274     g_y: 50,
33275     g_r: 30,
33276     opts:{
33277         //g_colors: this.colors,
33278         g_type: 'soft',
33279         g_gutter: '20%'
33280
33281     },
33282     title : false,
33283
33284     getAutoCreate : function(){
33285         
33286         var cfg = {
33287             tag: 'div',
33288             html : null
33289         };
33290         
33291         
33292         return  cfg;
33293     },
33294
33295     onRender : function(ct,position){
33296         
33297         
33298         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33299         
33300         if (typeof(Raphael) == 'undefined') {
33301             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33302             return;
33303         }
33304         
33305         this.raphael = Raphael(this.el.dom);
33306         
33307                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33308                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33309                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33310                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33311                 /*
33312                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33313                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33314                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33315                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33316                 
33317                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33318                 r.barchart(330, 10, 300, 220, data1);
33319                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33320                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33321                 */
33322                 
33323                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33324                 // r.barchart(30, 30, 560, 250,  xdata, {
33325                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33326                 //     axis : "0 0 1 1",
33327                 //     axisxlabels :  xdata
33328                 //     //yvalues : cols,
33329                    
33330                 // });
33331 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33332 //        
33333 //        this.load(null,xdata,{
33334 //                axis : "0 0 1 1",
33335 //                axisxlabels :  xdata
33336 //                });
33337
33338     },
33339
33340     load : function(graphtype,xdata,opts)
33341     {
33342         this.raphael.clear();
33343         if(!graphtype) {
33344             graphtype = this.graphtype;
33345         }
33346         if(!opts){
33347             opts = this.opts;
33348         }
33349         var r = this.raphael,
33350             fin = function () {
33351                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33352             },
33353             fout = function () {
33354                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33355             },
33356             pfin = function() {
33357                 this.sector.stop();
33358                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33359
33360                 if (this.label) {
33361                     this.label[0].stop();
33362                     this.label[0].attr({ r: 7.5 });
33363                     this.label[1].attr({ "font-weight": 800 });
33364                 }
33365             },
33366             pfout = function() {
33367                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33368
33369                 if (this.label) {
33370                     this.label[0].animate({ r: 5 }, 500, "bounce");
33371                     this.label[1].attr({ "font-weight": 400 });
33372                 }
33373             };
33374
33375         switch(graphtype){
33376             case 'bar':
33377                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33378                 break;
33379             case 'hbar':
33380                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33381                 break;
33382             case 'pie':
33383 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33384 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33385 //            
33386                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33387                 
33388                 break;
33389
33390         }
33391         
33392         if(this.title){
33393             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33394         }
33395         
33396     },
33397     
33398     setTitle: function(o)
33399     {
33400         this.title = o;
33401     },
33402     
33403     initEvents: function() {
33404         
33405         if(!this.href){
33406             this.el.on('click', this.onClick, this);
33407         }
33408     },
33409     
33410     onClick : function(e)
33411     {
33412         Roo.log('img onclick');
33413         this.fireEvent('click', this, e);
33414     }
33415    
33416 });
33417
33418  
33419 Roo.bootstrap.dash = {};/*
33420  * - LGPL
33421  *
33422  * numberBox
33423  * 
33424  */
33425 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33426
33427 /**
33428  * @class Roo.bootstrap.dash.NumberBox
33429  * @extends Roo.bootstrap.Component
33430  * Bootstrap NumberBox class
33431  * @cfg {String} headline Box headline
33432  * @cfg {String} content Box content
33433  * @cfg {String} icon Box icon
33434  * @cfg {String} footer Footer text
33435  * @cfg {String} fhref Footer href
33436  * 
33437  * @constructor
33438  * Create a new NumberBox
33439  * @param {Object} config The config object
33440  */
33441
33442
33443 Roo.bootstrap.dash.NumberBox = function(config){
33444     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33445     
33446 };
33447
33448 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33449     
33450     headline : '',
33451     content : '',
33452     icon : '',
33453     footer : '',
33454     fhref : '',
33455     ficon : '',
33456     
33457     getAutoCreate : function(){
33458         
33459         var cfg = {
33460             tag : 'div',
33461             cls : 'small-box ',
33462             cn : [
33463                 {
33464                     tag : 'div',
33465                     cls : 'inner',
33466                     cn :[
33467                         {
33468                             tag : 'h3',
33469                             cls : 'roo-headline',
33470                             html : this.headline
33471                         },
33472                         {
33473                             tag : 'p',
33474                             cls : 'roo-content',
33475                             html : this.content
33476                         }
33477                     ]
33478                 }
33479             ]
33480         };
33481         
33482         if(this.icon){
33483             cfg.cn.push({
33484                 tag : 'div',
33485                 cls : 'icon',
33486                 cn :[
33487                     {
33488                         tag : 'i',
33489                         cls : 'ion ' + this.icon
33490                     }
33491                 ]
33492             });
33493         }
33494         
33495         if(this.footer){
33496             var footer = {
33497                 tag : 'a',
33498                 cls : 'small-box-footer',
33499                 href : this.fhref || '#',
33500                 html : this.footer
33501             };
33502             
33503             cfg.cn.push(footer);
33504             
33505         }
33506         
33507         return  cfg;
33508     },
33509
33510     onRender : function(ct,position){
33511         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33512
33513
33514        
33515                 
33516     },
33517
33518     setHeadline: function (value)
33519     {
33520         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33521     },
33522     
33523     setFooter: function (value, href)
33524     {
33525         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33526         
33527         if(href){
33528             this.el.select('a.small-box-footer',true).first().attr('href', href);
33529         }
33530         
33531     },
33532
33533     setContent: function (value)
33534     {
33535         this.el.select('.roo-content',true).first().dom.innerHTML = value;
33536     },
33537
33538     initEvents: function() 
33539     {   
33540         
33541     }
33542     
33543 });
33544
33545  
33546 /*
33547  * - LGPL
33548  *
33549  * TabBox
33550  * 
33551  */
33552 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33553
33554 /**
33555  * @class Roo.bootstrap.dash.TabBox
33556  * @extends Roo.bootstrap.Component
33557  * @children Roo.bootstrap.dash.TabPane
33558  * Bootstrap TabBox class
33559  * @cfg {String} title Title of the TabBox
33560  * @cfg {String} icon Icon of the TabBox
33561  * @cfg {Boolean} showtabs (true|false) show the tabs default true
33562  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33563  * 
33564  * @constructor
33565  * Create a new TabBox
33566  * @param {Object} config The config object
33567  */
33568
33569
33570 Roo.bootstrap.dash.TabBox = function(config){
33571     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
33572     this.addEvents({
33573         // raw events
33574         /**
33575          * @event addpane
33576          * When a pane is added
33577          * @param {Roo.bootstrap.dash.TabPane} pane
33578          */
33579         "addpane" : true,
33580         /**
33581          * @event activatepane
33582          * When a pane is activated
33583          * @param {Roo.bootstrap.dash.TabPane} pane
33584          */
33585         "activatepane" : true
33586         
33587          
33588     });
33589     
33590     this.panes = [];
33591 };
33592
33593 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
33594
33595     title : '',
33596     icon : false,
33597     showtabs : true,
33598     tabScrollable : false,
33599     
33600     getChildContainer : function()
33601     {
33602         return this.el.select('.tab-content', true).first();
33603     },
33604     
33605     getAutoCreate : function(){
33606         
33607         var header = {
33608             tag: 'li',
33609             cls: 'pull-left header',
33610             html: this.title,
33611             cn : []
33612         };
33613         
33614         if(this.icon){
33615             header.cn.push({
33616                 tag: 'i',
33617                 cls: 'fa ' + this.icon
33618             });
33619         }
33620         
33621         var h = {
33622             tag: 'ul',
33623             cls: 'nav nav-tabs pull-right',
33624             cn: [
33625                 header
33626             ]
33627         };
33628         
33629         if(this.tabScrollable){
33630             h = {
33631                 tag: 'div',
33632                 cls: 'tab-header',
33633                 cn: [
33634                     {
33635                         tag: 'ul',
33636                         cls: 'nav nav-tabs pull-right',
33637                         cn: [
33638                             header
33639                         ]
33640                     }
33641                 ]
33642             };
33643         }
33644         
33645         var cfg = {
33646             tag: 'div',
33647             cls: 'nav-tabs-custom',
33648             cn: [
33649                 h,
33650                 {
33651                     tag: 'div',
33652                     cls: 'tab-content no-padding',
33653                     cn: []
33654                 }
33655             ]
33656         };
33657
33658         return  cfg;
33659     },
33660     initEvents : function()
33661     {
33662         //Roo.log('add add pane handler');
33663         this.on('addpane', this.onAddPane, this);
33664     },
33665      /**
33666      * Updates the box title
33667      * @param {String} html to set the title to.
33668      */
33669     setTitle : function(value)
33670     {
33671         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
33672     },
33673     onAddPane : function(pane)
33674     {
33675         this.panes.push(pane);
33676         //Roo.log('addpane');
33677         //Roo.log(pane);
33678         // tabs are rendere left to right..
33679         if(!this.showtabs){
33680             return;
33681         }
33682         
33683         var ctr = this.el.select('.nav-tabs', true).first();
33684          
33685          
33686         var existing = ctr.select('.nav-tab',true);
33687         var qty = existing.getCount();;
33688         
33689         
33690         var tab = ctr.createChild({
33691             tag : 'li',
33692             cls : 'nav-tab' + (qty ? '' : ' active'),
33693             cn : [
33694                 {
33695                     tag : 'a',
33696                     href:'#',
33697                     html : pane.title
33698                 }
33699             ]
33700         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
33701         pane.tab = tab;
33702         
33703         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
33704         if (!qty) {
33705             pane.el.addClass('active');
33706         }
33707         
33708                 
33709     },
33710     onTabClick : function(ev,un,ob,pane)
33711     {
33712         //Roo.log('tab - prev default');
33713         ev.preventDefault();
33714         
33715         
33716         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
33717         pane.tab.addClass('active');
33718         //Roo.log(pane.title);
33719         this.getChildContainer().select('.tab-pane',true).removeClass('active');
33720         // technically we should have a deactivate event.. but maybe add later.
33721         // and it should not de-activate the selected tab...
33722         this.fireEvent('activatepane', pane);
33723         pane.el.addClass('active');
33724         pane.fireEvent('activate');
33725         
33726         
33727     },
33728     
33729     getActivePane : function()
33730     {
33731         var r = false;
33732         Roo.each(this.panes, function(p) {
33733             if(p.el.hasClass('active')){
33734                 r = p;
33735                 return false;
33736             }
33737             
33738             return;
33739         });
33740         
33741         return r;
33742     }
33743     
33744     
33745 });
33746
33747  
33748 /*
33749  * - LGPL
33750  *
33751  * Tab pane
33752  * 
33753  */
33754 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33755 /**
33756  * @class Roo.bootstrap.TabPane
33757  * @extends Roo.bootstrap.Component
33758  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
33759  * Bootstrap TabPane class
33760  * @cfg {Boolean} active (false | true) Default false
33761  * @cfg {String} title title of panel
33762
33763  * 
33764  * @constructor
33765  * Create a new TabPane
33766  * @param {Object} config The config object
33767  */
33768
33769 Roo.bootstrap.dash.TabPane = function(config){
33770     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
33771     
33772     this.addEvents({
33773         // raw events
33774         /**
33775          * @event activate
33776          * When a pane is activated
33777          * @param {Roo.bootstrap.dash.TabPane} pane
33778          */
33779         "activate" : true
33780          
33781     });
33782 };
33783
33784 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
33785     
33786     active : false,
33787     title : '',
33788     
33789     // the tabBox that this is attached to.
33790     tab : false,
33791      
33792     getAutoCreate : function() 
33793     {
33794         var cfg = {
33795             tag: 'div',
33796             cls: 'tab-pane'
33797         };
33798         
33799         if(this.active){
33800             cfg.cls += ' active';
33801         }
33802         
33803         return cfg;
33804     },
33805     initEvents  : function()
33806     {
33807         //Roo.log('trigger add pane handler');
33808         this.parent().fireEvent('addpane', this)
33809     },
33810     
33811      /**
33812      * Updates the tab title 
33813      * @param {String} html to set the title to.
33814      */
33815     setTitle: function(str)
33816     {
33817         if (!this.tab) {
33818             return;
33819         }
33820         this.title = str;
33821         this.tab.select('a', true).first().dom.innerHTML = str;
33822         
33823     }
33824     
33825     
33826     
33827 });
33828
33829  
33830
33831
33832  /*
33833  * - LGPL
33834  *
33835  * Tooltip
33836  * 
33837  */
33838
33839 /**
33840  * @class Roo.bootstrap.Tooltip
33841  * Bootstrap Tooltip class
33842  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
33843  * to determine which dom element triggers the tooltip.
33844  * 
33845  * It needs to add support for additional attributes like tooltip-position
33846  * 
33847  * @constructor
33848  * Create a new Toolti
33849  * @param {Object} config The config object
33850  */
33851
33852 Roo.bootstrap.Tooltip = function(config){
33853     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
33854     
33855     this.alignment = Roo.bootstrap.Tooltip.alignment;
33856     
33857     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
33858         this.alignment = config.alignment;
33859     }
33860     
33861 };
33862
33863 Roo.apply(Roo.bootstrap.Tooltip, {
33864     /**
33865      * @function init initialize tooltip monitoring.
33866      * @static
33867      */
33868     currentEl : false,
33869     currentTip : false,
33870     currentRegion : false,
33871     
33872     //  init : delay?
33873     
33874     init : function()
33875     {
33876         Roo.get(document).on('mouseover', this.enter ,this);
33877         Roo.get(document).on('mouseout', this.leave, this);
33878          
33879         
33880         this.currentTip = new Roo.bootstrap.Tooltip();
33881     },
33882     
33883     enter : function(ev)
33884     {
33885         var dom = ev.getTarget();
33886         
33887         //Roo.log(['enter',dom]);
33888         var el = Roo.fly(dom);
33889         if (this.currentEl) {
33890             //Roo.log(dom);
33891             //Roo.log(this.currentEl);
33892             //Roo.log(this.currentEl.contains(dom));
33893             if (this.currentEl == el) {
33894                 return;
33895             }
33896             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
33897                 return;
33898             }
33899
33900         }
33901         
33902         if (this.currentTip.el) {
33903             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
33904         }    
33905         //Roo.log(ev);
33906         
33907         if(!el || el.dom == document){
33908             return;
33909         }
33910         
33911         var bindEl = el; 
33912         var pel = false;
33913         if (!el.attr('tooltip')) {
33914             pel = el.findParent("[tooltip]");
33915             if (pel) {
33916                 bindEl = Roo.get(pel);
33917             }
33918         }
33919         
33920        
33921         
33922         // you can not look for children, as if el is the body.. then everythign is the child..
33923         if (!pel && !el.attr('tooltip')) { //
33924             if (!el.select("[tooltip]").elements.length) {
33925                 return;
33926             }
33927             // is the mouse over this child...?
33928             bindEl = el.select("[tooltip]").first();
33929             var xy = ev.getXY();
33930             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
33931                 //Roo.log("not in region.");
33932                 return;
33933             }
33934             //Roo.log("child element over..");
33935             
33936         }
33937         this.currentEl = el;
33938         this.currentTip.bind(bindEl);
33939         this.currentRegion = Roo.lib.Region.getRegion(dom);
33940         this.currentTip.enter();
33941         
33942     },
33943     leave : function(ev)
33944     {
33945         var dom = ev.getTarget();
33946         //Roo.log(['leave',dom]);
33947         if (!this.currentEl) {
33948             return;
33949         }
33950         
33951         
33952         if (dom != this.currentEl.dom) {
33953             return;
33954         }
33955         var xy = ev.getXY();
33956         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
33957             return;
33958         }
33959         // only activate leave if mouse cursor is outside... bounding box..
33960         
33961         
33962         
33963         
33964         if (this.currentTip) {
33965             this.currentTip.leave();
33966         }
33967         //Roo.log('clear currentEl');
33968         this.currentEl = false;
33969         
33970         
33971     },
33972     alignment : {
33973         'left' : ['r-l', [-2,0], 'right'],
33974         'right' : ['l-r', [2,0], 'left'],
33975         'bottom' : ['t-b', [0,2], 'top'],
33976         'top' : [ 'b-t', [0,-2], 'bottom']
33977     }
33978     
33979 });
33980
33981
33982 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
33983     
33984     
33985     bindEl : false,
33986     
33987     delay : null, // can be { show : 300 , hide: 500}
33988     
33989     timeout : null,
33990     
33991     hoverState : null, //???
33992     
33993     placement : 'bottom', 
33994     
33995     alignment : false,
33996     
33997     getAutoCreate : function(){
33998     
33999         var cfg = {
34000            cls : 'tooltip',   
34001            role : 'tooltip',
34002            cn : [
34003                 {
34004                     cls : 'tooltip-arrow arrow'
34005                 },
34006                 {
34007                     cls : 'tooltip-inner'
34008                 }
34009            ]
34010         };
34011         
34012         return cfg;
34013     },
34014     bind : function(el)
34015     {
34016         this.bindEl = el;
34017     },
34018     
34019     initEvents : function()
34020     {
34021         this.arrowEl = this.el.select('.arrow', true).first();
34022         this.innerEl = this.el.select('.tooltip-inner', true).first();
34023     },
34024     
34025     enter : function () {
34026        
34027         if (this.timeout != null) {
34028             clearTimeout(this.timeout);
34029         }
34030         
34031         this.hoverState = 'in';
34032          //Roo.log("enter - show");
34033         if (!this.delay || !this.delay.show) {
34034             this.show();
34035             return;
34036         }
34037         var _t = this;
34038         this.timeout = setTimeout(function () {
34039             if (_t.hoverState == 'in') {
34040                 _t.show();
34041             }
34042         }, this.delay.show);
34043     },
34044     leave : function()
34045     {
34046         clearTimeout(this.timeout);
34047     
34048         this.hoverState = 'out';
34049          if (!this.delay || !this.delay.hide) {
34050             this.hide();
34051             return;
34052         }
34053        
34054         var _t = this;
34055         this.timeout = setTimeout(function () {
34056             //Roo.log("leave - timeout");
34057             
34058             if (_t.hoverState == 'out') {
34059                 _t.hide();
34060                 Roo.bootstrap.Tooltip.currentEl = false;
34061             }
34062         }, delay);
34063     },
34064     
34065     show : function (msg)
34066     {
34067         if (!this.el) {
34068             this.render(document.body);
34069         }
34070         // set content.
34071         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34072         
34073         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34074         
34075         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34076         
34077         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34078                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34079
34080         if(this.bindEl.attr('tooltip-class')) {
34081             this.el.addClass(this.bindEl.attr('tooltip-class'));
34082         }
34083         
34084         var placement = typeof this.placement == 'function' ?
34085             this.placement.call(this, this.el, on_el) :
34086             this.placement;
34087         
34088         if(this.bindEl.attr('tooltip-placement')) {
34089             placement = this.bindEl.attr('tooltip-placement');
34090         }
34091             
34092         var autoToken = /\s?auto?\s?/i;
34093         var autoPlace = autoToken.test(placement);
34094         if (autoPlace) {
34095             placement = placement.replace(autoToken, '') || 'top';
34096         }
34097         
34098         //this.el.detach()
34099         //this.el.setXY([0,0]);
34100         this.el.show();
34101         //this.el.dom.style.display='block';
34102         
34103         //this.el.appendTo(on_el);
34104         
34105         var p = this.getPosition();
34106         var box = this.el.getBox();
34107         
34108         if (autoPlace) {
34109             // fixme..
34110         }
34111         
34112         var align = this.alignment[placement];
34113         
34114         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34115         
34116         if(placement == 'top' || placement == 'bottom'){
34117             if(xy[0] < 0){
34118                 placement = 'right';
34119             }
34120             
34121             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34122                 placement = 'left';
34123             }
34124             
34125             var scroll = Roo.select('body', true).first().getScroll();
34126             
34127             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34128                 placement = 'top';
34129             }
34130             
34131             align = this.alignment[placement];
34132             
34133             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34134             
34135         }
34136         
34137         var elems = document.getElementsByTagName('div');
34138         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34139         for (var i = 0; i < elems.length; i++) {
34140           var zindex = Number.parseInt(
34141                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34142                 10
34143           );
34144           if (zindex > highest) {
34145             highest = zindex;
34146           }
34147         }
34148         
34149         
34150         
34151         this.el.dom.style.zIndex = highest;
34152         
34153         this.el.alignTo(this.bindEl, align[0],align[1]);
34154         //var arrow = this.el.select('.arrow',true).first();
34155         //arrow.set(align[2], 
34156         
34157         this.el.addClass(placement);
34158         this.el.addClass("bs-tooltip-"+ placement);
34159         
34160         this.el.addClass('in fade show');
34161         
34162         this.hoverState = null;
34163         
34164         if (this.el.hasClass('fade')) {
34165             // fade it?
34166         }
34167         
34168         
34169         
34170         
34171         
34172     },
34173     hide : function()
34174     {
34175          
34176         if (!this.el) {
34177             return;
34178         }
34179         //this.el.setXY([0,0]);
34180         if(this.bindEl.attr('tooltip-class')) {
34181             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34182         }
34183         this.el.removeClass(['show', 'in']);
34184         //this.el.hide();
34185         
34186     }
34187     
34188 });
34189  
34190
34191  /*
34192  * - LGPL
34193  *
34194  * Location Picker
34195  * 
34196  */
34197
34198 /**
34199  * @class Roo.bootstrap.LocationPicker
34200  * @extends Roo.bootstrap.Component
34201  * Bootstrap LocationPicker class
34202  * @cfg {Number} latitude Position when init default 0
34203  * @cfg {Number} longitude Position when init default 0
34204  * @cfg {Number} zoom default 15
34205  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34206  * @cfg {Boolean} mapTypeControl default false
34207  * @cfg {Boolean} disableDoubleClickZoom default false
34208  * @cfg {Boolean} scrollwheel default true
34209  * @cfg {Boolean} streetViewControl default false
34210  * @cfg {Number} radius default 0
34211  * @cfg {String} locationName
34212  * @cfg {Boolean} draggable default true
34213  * @cfg {Boolean} enableAutocomplete default false
34214  * @cfg {Boolean} enableReverseGeocode default true
34215  * @cfg {String} markerTitle
34216  * 
34217  * @constructor
34218  * Create a new LocationPicker
34219  * @param {Object} config The config object
34220  */
34221
34222
34223 Roo.bootstrap.LocationPicker = function(config){
34224     
34225     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34226     
34227     this.addEvents({
34228         /**
34229          * @event initial
34230          * Fires when the picker initialized.
34231          * @param {Roo.bootstrap.LocationPicker} this
34232          * @param {Google Location} location
34233          */
34234         initial : true,
34235         /**
34236          * @event positionchanged
34237          * Fires when the picker position changed.
34238          * @param {Roo.bootstrap.LocationPicker} this
34239          * @param {Google Location} location
34240          */
34241         positionchanged : true,
34242         /**
34243          * @event resize
34244          * Fires when the map resize.
34245          * @param {Roo.bootstrap.LocationPicker} this
34246          */
34247         resize : true,
34248         /**
34249          * @event show
34250          * Fires when the map show.
34251          * @param {Roo.bootstrap.LocationPicker} this
34252          */
34253         show : true,
34254         /**
34255          * @event hide
34256          * Fires when the map hide.
34257          * @param {Roo.bootstrap.LocationPicker} this
34258          */
34259         hide : true,
34260         /**
34261          * @event mapClick
34262          * Fires when click the map.
34263          * @param {Roo.bootstrap.LocationPicker} this
34264          * @param {Map event} e
34265          */
34266         mapClick : true,
34267         /**
34268          * @event mapRightClick
34269          * Fires when right click the map.
34270          * @param {Roo.bootstrap.LocationPicker} this
34271          * @param {Map event} e
34272          */
34273         mapRightClick : true,
34274         /**
34275          * @event markerClick
34276          * Fires when click the marker.
34277          * @param {Roo.bootstrap.LocationPicker} this
34278          * @param {Map event} e
34279          */
34280         markerClick : true,
34281         /**
34282          * @event markerRightClick
34283          * Fires when right click the marker.
34284          * @param {Roo.bootstrap.LocationPicker} this
34285          * @param {Map event} e
34286          */
34287         markerRightClick : true,
34288         /**
34289          * @event OverlayViewDraw
34290          * Fires when OverlayView Draw
34291          * @param {Roo.bootstrap.LocationPicker} this
34292          */
34293         OverlayViewDraw : true,
34294         /**
34295          * @event OverlayViewOnAdd
34296          * Fires when OverlayView Draw
34297          * @param {Roo.bootstrap.LocationPicker} this
34298          */
34299         OverlayViewOnAdd : true,
34300         /**
34301          * @event OverlayViewOnRemove
34302          * Fires when OverlayView Draw
34303          * @param {Roo.bootstrap.LocationPicker} this
34304          */
34305         OverlayViewOnRemove : true,
34306         /**
34307          * @event OverlayViewShow
34308          * Fires when OverlayView Draw
34309          * @param {Roo.bootstrap.LocationPicker} this
34310          * @param {Pixel} cpx
34311          */
34312         OverlayViewShow : true,
34313         /**
34314          * @event OverlayViewHide
34315          * Fires when OverlayView Draw
34316          * @param {Roo.bootstrap.LocationPicker} this
34317          */
34318         OverlayViewHide : true,
34319         /**
34320          * @event loadexception
34321          * Fires when load google lib failed.
34322          * @param {Roo.bootstrap.LocationPicker} this
34323          */
34324         loadexception : true
34325     });
34326         
34327 };
34328
34329 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34330     
34331     gMapContext: false,
34332     
34333     latitude: 0,
34334     longitude: 0,
34335     zoom: 15,
34336     mapTypeId: false,
34337     mapTypeControl: false,
34338     disableDoubleClickZoom: false,
34339     scrollwheel: true,
34340     streetViewControl: false,
34341     radius: 0,
34342     locationName: '',
34343     draggable: true,
34344     enableAutocomplete: false,
34345     enableReverseGeocode: true,
34346     markerTitle: '',
34347     
34348     getAutoCreate: function()
34349     {
34350
34351         var cfg = {
34352             tag: 'div',
34353             cls: 'roo-location-picker'
34354         };
34355         
34356         return cfg
34357     },
34358     
34359     initEvents: function(ct, position)
34360     {       
34361         if(!this.el.getWidth() || this.isApplied()){
34362             return;
34363         }
34364         
34365         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34366         
34367         this.initial();
34368     },
34369     
34370     initial: function()
34371     {
34372         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34373             this.fireEvent('loadexception', this);
34374             return;
34375         }
34376         
34377         if(!this.mapTypeId){
34378             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34379         }
34380         
34381         this.gMapContext = this.GMapContext();
34382         
34383         this.initOverlayView();
34384         
34385         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34386         
34387         var _this = this;
34388                 
34389         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34390             _this.setPosition(_this.gMapContext.marker.position);
34391         });
34392         
34393         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34394             _this.fireEvent('mapClick', this, event);
34395             
34396         });
34397
34398         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34399             _this.fireEvent('mapRightClick', this, event);
34400             
34401         });
34402         
34403         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34404             _this.fireEvent('markerClick', this, event);
34405             
34406         });
34407
34408         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34409             _this.fireEvent('markerRightClick', this, event);
34410             
34411         });
34412         
34413         this.setPosition(this.gMapContext.location);
34414         
34415         this.fireEvent('initial', this, this.gMapContext.location);
34416     },
34417     
34418     initOverlayView: function()
34419     {
34420         var _this = this;
34421         
34422         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34423             
34424             draw: function()
34425             {
34426                 _this.fireEvent('OverlayViewDraw', _this);
34427             },
34428             
34429             onAdd: function()
34430             {
34431                 _this.fireEvent('OverlayViewOnAdd', _this);
34432             },
34433             
34434             onRemove: function()
34435             {
34436                 _this.fireEvent('OverlayViewOnRemove', _this);
34437             },
34438             
34439             show: function(cpx)
34440             {
34441                 _this.fireEvent('OverlayViewShow', _this, cpx);
34442             },
34443             
34444             hide: function()
34445             {
34446                 _this.fireEvent('OverlayViewHide', _this);
34447             }
34448             
34449         });
34450     },
34451     
34452     fromLatLngToContainerPixel: function(event)
34453     {
34454         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34455     },
34456     
34457     isApplied: function() 
34458     {
34459         return this.getGmapContext() == false ? false : true;
34460     },
34461     
34462     getGmapContext: function() 
34463     {
34464         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34465     },
34466     
34467     GMapContext: function() 
34468     {
34469         var position = new google.maps.LatLng(this.latitude, this.longitude);
34470         
34471         var _map = new google.maps.Map(this.el.dom, {
34472             center: position,
34473             zoom: this.zoom,
34474             mapTypeId: this.mapTypeId,
34475             mapTypeControl: this.mapTypeControl,
34476             disableDoubleClickZoom: this.disableDoubleClickZoom,
34477             scrollwheel: this.scrollwheel,
34478             streetViewControl: this.streetViewControl,
34479             locationName: this.locationName,
34480             draggable: this.draggable,
34481             enableAutocomplete: this.enableAutocomplete,
34482             enableReverseGeocode: this.enableReverseGeocode
34483         });
34484         
34485         var _marker = new google.maps.Marker({
34486             position: position,
34487             map: _map,
34488             title: this.markerTitle,
34489             draggable: this.draggable
34490         });
34491         
34492         return {
34493             map: _map,
34494             marker: _marker,
34495             circle: null,
34496             location: position,
34497             radius: this.radius,
34498             locationName: this.locationName,
34499             addressComponents: {
34500                 formatted_address: null,
34501                 addressLine1: null,
34502                 addressLine2: null,
34503                 streetName: null,
34504                 streetNumber: null,
34505                 city: null,
34506                 district: null,
34507                 state: null,
34508                 stateOrProvince: null
34509             },
34510             settings: this,
34511             domContainer: this.el.dom,
34512             geodecoder: new google.maps.Geocoder()
34513         };
34514     },
34515     
34516     drawCircle: function(center, radius, options) 
34517     {
34518         if (this.gMapContext.circle != null) {
34519             this.gMapContext.circle.setMap(null);
34520         }
34521         if (radius > 0) {
34522             radius *= 1;
34523             options = Roo.apply({}, options, {
34524                 strokeColor: "#0000FF",
34525                 strokeOpacity: .35,
34526                 strokeWeight: 2,
34527                 fillColor: "#0000FF",
34528                 fillOpacity: .2
34529             });
34530             
34531             options.map = this.gMapContext.map;
34532             options.radius = radius;
34533             options.center = center;
34534             this.gMapContext.circle = new google.maps.Circle(options);
34535             return this.gMapContext.circle;
34536         }
34537         
34538         return null;
34539     },
34540     
34541     setPosition: function(location) 
34542     {
34543         this.gMapContext.location = location;
34544         this.gMapContext.marker.setPosition(location);
34545         this.gMapContext.map.panTo(location);
34546         this.drawCircle(location, this.gMapContext.radius, {});
34547         
34548         var _this = this;
34549         
34550         if (this.gMapContext.settings.enableReverseGeocode) {
34551             this.gMapContext.geodecoder.geocode({
34552                 latLng: this.gMapContext.location
34553             }, function(results, status) {
34554                 
34555                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34556                     _this.gMapContext.locationName = results[0].formatted_address;
34557                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34558                     
34559                     _this.fireEvent('positionchanged', this, location);
34560                 }
34561             });
34562             
34563             return;
34564         }
34565         
34566         this.fireEvent('positionchanged', this, location);
34567     },
34568     
34569     resize: function()
34570     {
34571         google.maps.event.trigger(this.gMapContext.map, "resize");
34572         
34573         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
34574         
34575         this.fireEvent('resize', this);
34576     },
34577     
34578     setPositionByLatLng: function(latitude, longitude)
34579     {
34580         this.setPosition(new google.maps.LatLng(latitude, longitude));
34581     },
34582     
34583     getCurrentPosition: function() 
34584     {
34585         return {
34586             latitude: this.gMapContext.location.lat(),
34587             longitude: this.gMapContext.location.lng()
34588         };
34589     },
34590     
34591     getAddressName: function() 
34592     {
34593         return this.gMapContext.locationName;
34594     },
34595     
34596     getAddressComponents: function() 
34597     {
34598         return this.gMapContext.addressComponents;
34599     },
34600     
34601     address_component_from_google_geocode: function(address_components) 
34602     {
34603         var result = {};
34604         
34605         for (var i = 0; i < address_components.length; i++) {
34606             var component = address_components[i];
34607             if (component.types.indexOf("postal_code") >= 0) {
34608                 result.postalCode = component.short_name;
34609             } else if (component.types.indexOf("street_number") >= 0) {
34610                 result.streetNumber = component.short_name;
34611             } else if (component.types.indexOf("route") >= 0) {
34612                 result.streetName = component.short_name;
34613             } else if (component.types.indexOf("neighborhood") >= 0) {
34614                 result.city = component.short_name;
34615             } else if (component.types.indexOf("locality") >= 0) {
34616                 result.city = component.short_name;
34617             } else if (component.types.indexOf("sublocality") >= 0) {
34618                 result.district = component.short_name;
34619             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
34620                 result.stateOrProvince = component.short_name;
34621             } else if (component.types.indexOf("country") >= 0) {
34622                 result.country = component.short_name;
34623             }
34624         }
34625         
34626         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
34627         result.addressLine2 = "";
34628         return result;
34629     },
34630     
34631     setZoomLevel: function(zoom)
34632     {
34633         this.gMapContext.map.setZoom(zoom);
34634     },
34635     
34636     show: function()
34637     {
34638         if(!this.el){
34639             return;
34640         }
34641         
34642         this.el.show();
34643         
34644         this.resize();
34645         
34646         this.fireEvent('show', this);
34647     },
34648     
34649     hide: function()
34650     {
34651         if(!this.el){
34652             return;
34653         }
34654         
34655         this.el.hide();
34656         
34657         this.fireEvent('hide', this);
34658     }
34659     
34660 });
34661
34662 Roo.apply(Roo.bootstrap.LocationPicker, {
34663     
34664     OverlayView : function(map, options)
34665     {
34666         options = options || {};
34667         
34668         this.setMap(map);
34669     }
34670     
34671     
34672 });/**
34673  * @class Roo.bootstrap.Alert
34674  * @extends Roo.bootstrap.Component
34675  * Bootstrap Alert class - shows an alert area box
34676  * eg
34677  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
34678   Enter a valid email address
34679 </div>
34680  * @licence LGPL
34681  * @cfg {String} title The title of alert
34682  * @cfg {String} html The content of alert
34683  * @cfg {String} weight (success|info|warning|danger) Weight of the message
34684  * @cfg {String} fa font-awesomeicon
34685  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
34686  * @cfg {Boolean} close true to show a x closer
34687  * 
34688  * 
34689  * @constructor
34690  * Create a new alert
34691  * @param {Object} config The config object
34692  */
34693
34694
34695 Roo.bootstrap.Alert = function(config){
34696     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
34697     
34698 };
34699
34700 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
34701     
34702     title: '',
34703     html: '',
34704     weight: false,
34705     fa: false,
34706     faicon: false, // BC
34707     close : false,
34708     
34709     
34710     getAutoCreate : function()
34711     {
34712         
34713         var cfg = {
34714             tag : 'div',
34715             cls : 'alert',
34716             cn : [
34717                 {
34718                     tag: 'button',
34719                     type :  "button",
34720                     cls: "close",
34721                     html : '×',
34722                     style : this.close ? '' : 'display:none'
34723                 },
34724                 {
34725                     tag : 'i',
34726                     cls : 'roo-alert-icon'
34727                     
34728                 },
34729                 {
34730                     tag : 'b',
34731                     cls : 'roo-alert-title',
34732                     html : this.title
34733                 },
34734                 {
34735                     tag : 'span',
34736                     cls : 'roo-alert-text',
34737                     html : this.html
34738                 }
34739             ]
34740         };
34741         
34742         if(this.faicon){
34743             cfg.cn[0].cls += ' fa ' + this.faicon;
34744         }
34745         if(this.fa){
34746             cfg.cn[0].cls += ' fa ' + this.fa;
34747         }
34748         
34749         if(this.weight){
34750             cfg.cls += ' alert-' + this.weight;
34751         }
34752         
34753         return cfg;
34754     },
34755     
34756     initEvents: function() 
34757     {
34758         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34759         this.titleEl =  this.el.select('.roo-alert-title',true).first();
34760         this.iconEl = this.el.select('.roo-alert-icon',true).first();
34761         this.htmlEl = this.el.select('.roo-alert-text',true).first();
34762         if (this.seconds > 0) {
34763             this.hide.defer(this.seconds, this);
34764         }
34765     },
34766     /**
34767      * Set the Title Message HTML
34768      * @param {String} html
34769      */
34770     setTitle : function(str)
34771     {
34772         this.titleEl.dom.innerHTML = str;
34773     },
34774      
34775      /**
34776      * Set the Body Message HTML
34777      * @param {String} html
34778      */
34779     setHtml : function(str)
34780     {
34781         this.htmlEl.dom.innerHTML = str;
34782     },
34783     /**
34784      * Set the Weight of the alert
34785      * @param {String} (success|info|warning|danger) weight
34786      */
34787     
34788     setWeight : function(weight)
34789     {
34790         if(this.weight){
34791             this.el.removeClass('alert-' + this.weight);
34792         }
34793         
34794         this.weight = weight;
34795         
34796         this.el.addClass('alert-' + this.weight);
34797     },
34798       /**
34799      * Set the Icon of the alert
34800      * @param {String} see fontawsome names (name without the 'fa-' bit)
34801      */
34802     setIcon : function(icon)
34803     {
34804         if(this.faicon){
34805             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
34806         }
34807         
34808         this.faicon = icon;
34809         
34810         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
34811     },
34812     /**
34813      * Hide the Alert
34814      */
34815     hide: function() 
34816     {
34817         this.el.hide();   
34818     },
34819     /**
34820      * Show the Alert
34821      */
34822     show: function() 
34823     {  
34824         this.el.show();   
34825     }
34826     
34827 });
34828
34829  
34830 /*
34831 * Licence: LGPL
34832 */
34833
34834 /**
34835  * @class Roo.bootstrap.UploadCropbox
34836  * @extends Roo.bootstrap.Component
34837  * Bootstrap UploadCropbox class
34838  * @cfg {String} emptyText show when image has been loaded
34839  * @cfg {String} rotateNotify show when image too small to rotate
34840  * @cfg {Number} errorTimeout default 3000
34841  * @cfg {Number} minWidth default 300
34842  * @cfg {Number} minHeight default 300
34843  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
34844  * @cfg {Boolean} isDocument (true|false) default false
34845  * @cfg {String} url action url
34846  * @cfg {String} paramName default 'imageUpload'
34847  * @cfg {String} method default POST
34848  * @cfg {Boolean} loadMask (true|false) default true
34849  * @cfg {Boolean} loadingText default 'Loading...'
34850  * 
34851  * @constructor
34852  * Create a new UploadCropbox
34853  * @param {Object} config The config object
34854  */
34855
34856 Roo.bootstrap.UploadCropbox = function(config){
34857     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
34858     
34859     this.addEvents({
34860         /**
34861          * @event beforeselectfile
34862          * Fire before select file
34863          * @param {Roo.bootstrap.UploadCropbox} this
34864          */
34865         "beforeselectfile" : true,
34866         /**
34867          * @event initial
34868          * Fire after initEvent
34869          * @param {Roo.bootstrap.UploadCropbox} this
34870          */
34871         "initial" : true,
34872         /**
34873          * @event crop
34874          * Fire after initEvent
34875          * @param {Roo.bootstrap.UploadCropbox} this
34876          * @param {String} data
34877          */
34878         "crop" : true,
34879         /**
34880          * @event prepare
34881          * Fire when preparing the file data
34882          * @param {Roo.bootstrap.UploadCropbox} this
34883          * @param {Object} file
34884          */
34885         "prepare" : true,
34886         /**
34887          * @event exception
34888          * Fire when get exception
34889          * @param {Roo.bootstrap.UploadCropbox} this
34890          * @param {XMLHttpRequest} xhr
34891          */
34892         "exception" : true,
34893         /**
34894          * @event beforeloadcanvas
34895          * Fire before load the canvas
34896          * @param {Roo.bootstrap.UploadCropbox} this
34897          * @param {String} src
34898          */
34899         "beforeloadcanvas" : true,
34900         /**
34901          * @event trash
34902          * Fire when trash image
34903          * @param {Roo.bootstrap.UploadCropbox} this
34904          */
34905         "trash" : true,
34906         /**
34907          * @event download
34908          * Fire when download the image
34909          * @param {Roo.bootstrap.UploadCropbox} this
34910          */
34911         "download" : true,
34912         /**
34913          * @event footerbuttonclick
34914          * Fire when footerbuttonclick
34915          * @param {Roo.bootstrap.UploadCropbox} this
34916          * @param {String} type
34917          */
34918         "footerbuttonclick" : true,
34919         /**
34920          * @event resize
34921          * Fire when resize
34922          * @param {Roo.bootstrap.UploadCropbox} this
34923          */
34924         "resize" : true,
34925         /**
34926          * @event rotate
34927          * Fire when rotate the image
34928          * @param {Roo.bootstrap.UploadCropbox} this
34929          * @param {String} pos
34930          */
34931         "rotate" : true,
34932         /**
34933          * @event inspect
34934          * Fire when inspect the file
34935          * @param {Roo.bootstrap.UploadCropbox} this
34936          * @param {Object} file
34937          */
34938         "inspect" : true,
34939         /**
34940          * @event upload
34941          * Fire when xhr upload the file
34942          * @param {Roo.bootstrap.UploadCropbox} this
34943          * @param {Object} data
34944          */
34945         "upload" : true,
34946         /**
34947          * @event arrange
34948          * Fire when arrange the file data
34949          * @param {Roo.bootstrap.UploadCropbox} this
34950          * @param {Object} formData
34951          */
34952         "arrange" : true
34953     });
34954     
34955     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
34956 };
34957
34958 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
34959     
34960     emptyText : 'Click to upload image',
34961     rotateNotify : 'Image is too small to rotate',
34962     errorTimeout : 3000,
34963     scale : 0,
34964     baseScale : 1,
34965     rotate : 0,
34966     dragable : false,
34967     pinching : false,
34968     mouseX : 0,
34969     mouseY : 0,
34970     cropData : false,
34971     minWidth : 300,
34972     minHeight : 300,
34973     file : false,
34974     exif : {},
34975     baseRotate : 1,
34976     cropType : 'image/jpeg',
34977     buttons : false,
34978     canvasLoaded : false,
34979     isDocument : false,
34980     method : 'POST',
34981     paramName : 'imageUpload',
34982     loadMask : true,
34983     loadingText : 'Loading...',
34984     maskEl : false,
34985     
34986     getAutoCreate : function()
34987     {
34988         var cfg = {
34989             tag : 'div',
34990             cls : 'roo-upload-cropbox',
34991             cn : [
34992                 {
34993                     tag : 'input',
34994                     cls : 'roo-upload-cropbox-selector',
34995                     type : 'file'
34996                 },
34997                 {
34998                     tag : 'div',
34999                     cls : 'roo-upload-cropbox-body',
35000                     style : 'cursor:pointer',
35001                     cn : [
35002                         {
35003                             tag : 'div',
35004                             cls : 'roo-upload-cropbox-preview'
35005                         },
35006                         {
35007                             tag : 'div',
35008                             cls : 'roo-upload-cropbox-thumb'
35009                         },
35010                         {
35011                             tag : 'div',
35012                             cls : 'roo-upload-cropbox-empty-notify',
35013                             html : this.emptyText
35014                         },
35015                         {
35016                             tag : 'div',
35017                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35018                             html : this.rotateNotify
35019                         }
35020                     ]
35021                 },
35022                 {
35023                     tag : 'div',
35024                     cls : 'roo-upload-cropbox-footer',
35025                     cn : {
35026                         tag : 'div',
35027                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35028                         cn : []
35029                     }
35030                 }
35031             ]
35032         };
35033         
35034         return cfg;
35035     },
35036     
35037     onRender : function(ct, position)
35038     {
35039         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35040         
35041         if (this.buttons.length) {
35042             
35043             Roo.each(this.buttons, function(bb) {
35044                 
35045                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35046                 
35047                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35048                 
35049             }, this);
35050         }
35051         
35052         if(this.loadMask){
35053             this.maskEl = this.el;
35054         }
35055     },
35056     
35057     initEvents : function()
35058     {
35059         this.urlAPI = (window.createObjectURL && window) || 
35060                                 (window.URL && URL.revokeObjectURL && URL) || 
35061                                 (window.webkitURL && webkitURL);
35062                         
35063         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35064         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35065         
35066         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35067         this.selectorEl.hide();
35068         
35069         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35070         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35071         
35072         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35073         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35074         this.thumbEl.hide();
35075         
35076         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35077         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35078         
35079         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35080         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35081         this.errorEl.hide();
35082         
35083         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35084         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35085         this.footerEl.hide();
35086         
35087         this.setThumbBoxSize();
35088         
35089         this.bind();
35090         
35091         this.resize();
35092         
35093         this.fireEvent('initial', this);
35094     },
35095
35096     bind : function()
35097     {
35098         var _this = this;
35099         
35100         window.addEventListener("resize", function() { _this.resize(); } );
35101         
35102         this.bodyEl.on('click', this.beforeSelectFile, this);
35103         
35104         if(Roo.isTouch){
35105             this.bodyEl.on('touchstart', this.onTouchStart, this);
35106             this.bodyEl.on('touchmove', this.onTouchMove, this);
35107             this.bodyEl.on('touchend', this.onTouchEnd, this);
35108         }
35109         
35110         if(!Roo.isTouch){
35111             this.bodyEl.on('mousedown', this.onMouseDown, this);
35112             this.bodyEl.on('mousemove', this.onMouseMove, this);
35113             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35114             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35115             Roo.get(document).on('mouseup', this.onMouseUp, this);
35116         }
35117         
35118         this.selectorEl.on('change', this.onFileSelected, this);
35119     },
35120     
35121     reset : function()
35122     {    
35123         this.scale = 0;
35124         this.baseScale = 1;
35125         this.rotate = 0;
35126         this.baseRotate = 1;
35127         this.dragable = false;
35128         this.pinching = false;
35129         this.mouseX = 0;
35130         this.mouseY = 0;
35131         this.cropData = false;
35132         this.notifyEl.dom.innerHTML = this.emptyText;
35133         
35134         this.selectorEl.dom.value = '';
35135         
35136     },
35137     
35138     resize : function()
35139     {
35140         if(this.fireEvent('resize', this) != false){
35141             this.setThumbBoxPosition();
35142             this.setCanvasPosition();
35143         }
35144     },
35145     
35146     onFooterButtonClick : function(e, el, o, type)
35147     {
35148         switch (type) {
35149             case 'rotate-left' :
35150                 this.onRotateLeft(e);
35151                 break;
35152             case 'rotate-right' :
35153                 this.onRotateRight(e);
35154                 break;
35155             case 'picture' :
35156                 this.beforeSelectFile(e);
35157                 break;
35158             case 'trash' :
35159                 this.trash(e);
35160                 break;
35161             case 'crop' :
35162                 this.crop(e);
35163                 break;
35164             case 'download' :
35165                 this.download(e);
35166                 break;
35167             default :
35168                 break;
35169         }
35170         
35171         this.fireEvent('footerbuttonclick', this, type);
35172     },
35173     
35174     beforeSelectFile : function(e)
35175     {
35176         e.preventDefault();
35177         
35178         if(this.fireEvent('beforeselectfile', this) != false){
35179             this.selectorEl.dom.click();
35180         }
35181     },
35182     
35183     onFileSelected : function(e)
35184     {
35185         e.preventDefault();
35186         
35187         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35188             return;
35189         }
35190         
35191         var file = this.selectorEl.dom.files[0];
35192         
35193         if(this.fireEvent('inspect', this, file) != false){
35194             this.prepare(file);
35195         }
35196         
35197     },
35198     
35199     trash : function(e)
35200     {
35201         this.fireEvent('trash', this);
35202     },
35203     
35204     download : function(e)
35205     {
35206         this.fireEvent('download', this);
35207     },
35208     
35209     loadCanvas : function(src)
35210     {   
35211         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35212             
35213             this.reset();
35214             
35215             this.imageEl = document.createElement('img');
35216             
35217             var _this = this;
35218             
35219             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35220             
35221             this.imageEl.src = src;
35222         }
35223     },
35224     
35225     onLoadCanvas : function()
35226     {   
35227         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35228         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35229         
35230         this.bodyEl.un('click', this.beforeSelectFile, this);
35231         
35232         this.notifyEl.hide();
35233         this.thumbEl.show();
35234         this.footerEl.show();
35235         
35236         this.baseRotateLevel();
35237         
35238         if(this.isDocument){
35239             this.setThumbBoxSize();
35240         }
35241         
35242         this.setThumbBoxPosition();
35243         
35244         this.baseScaleLevel();
35245         
35246         this.draw();
35247         
35248         this.resize();
35249         
35250         this.canvasLoaded = true;
35251         
35252         if(this.loadMask){
35253             this.maskEl.unmask();
35254         }
35255         
35256     },
35257     
35258     setCanvasPosition : function()
35259     {   
35260         if(!this.canvasEl){
35261             return;
35262         }
35263         
35264         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35265         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35266         
35267         this.previewEl.setLeft(pw);
35268         this.previewEl.setTop(ph);
35269         
35270     },
35271     
35272     onMouseDown : function(e)
35273     {   
35274         e.stopEvent();
35275         
35276         this.dragable = true;
35277         this.pinching = false;
35278         
35279         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35280             this.dragable = false;
35281             return;
35282         }
35283         
35284         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35285         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35286         
35287     },
35288     
35289     onMouseMove : function(e)
35290     {   
35291         e.stopEvent();
35292         
35293         if(!this.canvasLoaded){
35294             return;
35295         }
35296         
35297         if (!this.dragable){
35298             return;
35299         }
35300         
35301         var minX = Math.ceil(this.thumbEl.getLeft(true));
35302         var minY = Math.ceil(this.thumbEl.getTop(true));
35303         
35304         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35305         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35306         
35307         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35308         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35309         
35310         x = x - this.mouseX;
35311         y = y - this.mouseY;
35312         
35313         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35314         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35315         
35316         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35317         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35318         
35319         this.previewEl.setLeft(bgX);
35320         this.previewEl.setTop(bgY);
35321         
35322         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35323         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35324     },
35325     
35326     onMouseUp : function(e)
35327     {   
35328         e.stopEvent();
35329         
35330         this.dragable = false;
35331     },
35332     
35333     onMouseWheel : function(e)
35334     {   
35335         e.stopEvent();
35336         
35337         this.startScale = this.scale;
35338         
35339         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35340         
35341         if(!this.zoomable()){
35342             this.scale = this.startScale;
35343             return;
35344         }
35345         
35346         this.draw();
35347         
35348         return;
35349     },
35350     
35351     zoomable : function()
35352     {
35353         var minScale = this.thumbEl.getWidth() / this.minWidth;
35354         
35355         if(this.minWidth < this.minHeight){
35356             minScale = this.thumbEl.getHeight() / this.minHeight;
35357         }
35358         
35359         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35360         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35361         
35362         if(
35363                 this.isDocument &&
35364                 (this.rotate == 0 || this.rotate == 180) && 
35365                 (
35366                     width > this.imageEl.OriginWidth || 
35367                     height > this.imageEl.OriginHeight ||
35368                     (width < this.minWidth && height < this.minHeight)
35369                 )
35370         ){
35371             return false;
35372         }
35373         
35374         if(
35375                 this.isDocument &&
35376                 (this.rotate == 90 || this.rotate == 270) && 
35377                 (
35378                     width > this.imageEl.OriginWidth || 
35379                     height > this.imageEl.OriginHeight ||
35380                     (width < this.minHeight && height < this.minWidth)
35381                 )
35382         ){
35383             return false;
35384         }
35385         
35386         if(
35387                 !this.isDocument &&
35388                 (this.rotate == 0 || this.rotate == 180) && 
35389                 (
35390                     width < this.minWidth || 
35391                     width > this.imageEl.OriginWidth || 
35392                     height < this.minHeight || 
35393                     height > this.imageEl.OriginHeight
35394                 )
35395         ){
35396             return false;
35397         }
35398         
35399         if(
35400                 !this.isDocument &&
35401                 (this.rotate == 90 || this.rotate == 270) && 
35402                 (
35403                     width < this.minHeight || 
35404                     width > this.imageEl.OriginWidth || 
35405                     height < this.minWidth || 
35406                     height > this.imageEl.OriginHeight
35407                 )
35408         ){
35409             return false;
35410         }
35411         
35412         return true;
35413         
35414     },
35415     
35416     onRotateLeft : function(e)
35417     {   
35418         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35419             
35420             var minScale = this.thumbEl.getWidth() / this.minWidth;
35421             
35422             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35423             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35424             
35425             this.startScale = this.scale;
35426             
35427             while (this.getScaleLevel() < minScale){
35428             
35429                 this.scale = this.scale + 1;
35430                 
35431                 if(!this.zoomable()){
35432                     break;
35433                 }
35434                 
35435                 if(
35436                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35437                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35438                 ){
35439                     continue;
35440                 }
35441                 
35442                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35443
35444                 this.draw();
35445                 
35446                 return;
35447             }
35448             
35449             this.scale = this.startScale;
35450             
35451             this.onRotateFail();
35452             
35453             return false;
35454         }
35455         
35456         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35457
35458         if(this.isDocument){
35459             this.setThumbBoxSize();
35460             this.setThumbBoxPosition();
35461             this.setCanvasPosition();
35462         }
35463         
35464         this.draw();
35465         
35466         this.fireEvent('rotate', this, 'left');
35467         
35468     },
35469     
35470     onRotateRight : function(e)
35471     {
35472         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35473             
35474             var minScale = this.thumbEl.getWidth() / this.minWidth;
35475         
35476             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35477             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35478             
35479             this.startScale = this.scale;
35480             
35481             while (this.getScaleLevel() < minScale){
35482             
35483                 this.scale = this.scale + 1;
35484                 
35485                 if(!this.zoomable()){
35486                     break;
35487                 }
35488                 
35489                 if(
35490                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35491                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35492                 ){
35493                     continue;
35494                 }
35495                 
35496                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35497
35498                 this.draw();
35499                 
35500                 return;
35501             }
35502             
35503             this.scale = this.startScale;
35504             
35505             this.onRotateFail();
35506             
35507             return false;
35508         }
35509         
35510         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35511
35512         if(this.isDocument){
35513             this.setThumbBoxSize();
35514             this.setThumbBoxPosition();
35515             this.setCanvasPosition();
35516         }
35517         
35518         this.draw();
35519         
35520         this.fireEvent('rotate', this, 'right');
35521     },
35522     
35523     onRotateFail : function()
35524     {
35525         this.errorEl.show(true);
35526         
35527         var _this = this;
35528         
35529         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35530     },
35531     
35532     draw : function()
35533     {
35534         this.previewEl.dom.innerHTML = '';
35535         
35536         var canvasEl = document.createElement("canvas");
35537         
35538         var contextEl = canvasEl.getContext("2d");
35539         
35540         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35541         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35542         var center = this.imageEl.OriginWidth / 2;
35543         
35544         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35545             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35546             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35547             center = this.imageEl.OriginHeight / 2;
35548         }
35549         
35550         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35551         
35552         contextEl.translate(center, center);
35553         contextEl.rotate(this.rotate * Math.PI / 180);
35554
35555         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35556         
35557         this.canvasEl = document.createElement("canvas");
35558         
35559         this.contextEl = this.canvasEl.getContext("2d");
35560         
35561         switch (this.rotate) {
35562             case 0 :
35563                 
35564                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35565                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35566                 
35567                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35568                 
35569                 break;
35570             case 90 : 
35571                 
35572                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35573                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35574                 
35575                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35576                     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);
35577                     break;
35578                 }
35579                 
35580                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35581                 
35582                 break;
35583             case 180 :
35584                 
35585                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35586                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35587                 
35588                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35589                     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);
35590                     break;
35591                 }
35592                 
35593                 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);
35594                 
35595                 break;
35596             case 270 :
35597                 
35598                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35599                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35600         
35601                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35602                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35603                     break;
35604                 }
35605                 
35606                 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);
35607                 
35608                 break;
35609             default : 
35610                 break;
35611         }
35612         
35613         this.previewEl.appendChild(this.canvasEl);
35614         
35615         this.setCanvasPosition();
35616     },
35617     
35618     crop : function()
35619     {
35620         if(!this.canvasLoaded){
35621             return;
35622         }
35623         
35624         var imageCanvas = document.createElement("canvas");
35625         
35626         var imageContext = imageCanvas.getContext("2d");
35627         
35628         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35629         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35630         
35631         var center = imageCanvas.width / 2;
35632         
35633         imageContext.translate(center, center);
35634         
35635         imageContext.rotate(this.rotate * Math.PI / 180);
35636         
35637         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35638         
35639         var canvas = document.createElement("canvas");
35640         
35641         var context = canvas.getContext("2d");
35642                 
35643         canvas.width = this.minWidth;
35644         canvas.height = this.minHeight;
35645
35646         switch (this.rotate) {
35647             case 0 :
35648                 
35649                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35650                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35651                 
35652                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35653                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35654                 
35655                 var targetWidth = this.minWidth - 2 * x;
35656                 var targetHeight = this.minHeight - 2 * y;
35657                 
35658                 var scale = 1;
35659                 
35660                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35661                     scale = targetWidth / width;
35662                 }
35663                 
35664                 if(x > 0 && y == 0){
35665                     scale = targetHeight / height;
35666                 }
35667                 
35668                 if(x > 0 && y > 0){
35669                     scale = targetWidth / width;
35670                     
35671                     if(width < height){
35672                         scale = targetHeight / height;
35673                     }
35674                 }
35675                 
35676                 context.scale(scale, scale);
35677                 
35678                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35679                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35680
35681                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35682                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35683
35684                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35685                 
35686                 break;
35687             case 90 : 
35688                 
35689                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35690                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35691                 
35692                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35693                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35694                 
35695                 var targetWidth = this.minWidth - 2 * x;
35696                 var targetHeight = this.minHeight - 2 * y;
35697                 
35698                 var scale = 1;
35699                 
35700                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35701                     scale = targetWidth / width;
35702                 }
35703                 
35704                 if(x > 0 && y == 0){
35705                     scale = targetHeight / height;
35706                 }
35707                 
35708                 if(x > 0 && y > 0){
35709                     scale = targetWidth / width;
35710                     
35711                     if(width < height){
35712                         scale = targetHeight / height;
35713                     }
35714                 }
35715                 
35716                 context.scale(scale, scale);
35717                 
35718                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35719                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35720
35721                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35722                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35723                 
35724                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35725                 
35726                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35727                 
35728                 break;
35729             case 180 :
35730                 
35731                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35732                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35733                 
35734                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35735                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35736                 
35737                 var targetWidth = this.minWidth - 2 * x;
35738                 var targetHeight = this.minHeight - 2 * y;
35739                 
35740                 var scale = 1;
35741                 
35742                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35743                     scale = targetWidth / width;
35744                 }
35745                 
35746                 if(x > 0 && y == 0){
35747                     scale = targetHeight / height;
35748                 }
35749                 
35750                 if(x > 0 && y > 0){
35751                     scale = targetWidth / width;
35752                     
35753                     if(width < height){
35754                         scale = targetHeight / height;
35755                     }
35756                 }
35757                 
35758                 context.scale(scale, scale);
35759                 
35760                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35761                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35762
35763                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35764                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35765
35766                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35767                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35768                 
35769                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35770                 
35771                 break;
35772             case 270 :
35773                 
35774                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35775                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35776                 
35777                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35778                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35779                 
35780                 var targetWidth = this.minWidth - 2 * x;
35781                 var targetHeight = this.minHeight - 2 * y;
35782                 
35783                 var scale = 1;
35784                 
35785                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35786                     scale = targetWidth / width;
35787                 }
35788                 
35789                 if(x > 0 && y == 0){
35790                     scale = targetHeight / height;
35791                 }
35792                 
35793                 if(x > 0 && y > 0){
35794                     scale = targetWidth / width;
35795                     
35796                     if(width < height){
35797                         scale = targetHeight / height;
35798                     }
35799                 }
35800                 
35801                 context.scale(scale, scale);
35802                 
35803                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35804                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35805
35806                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35807                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35808                 
35809                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35810                 
35811                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35812                 
35813                 break;
35814             default : 
35815                 break;
35816         }
35817         
35818         this.cropData = canvas.toDataURL(this.cropType);
35819         
35820         if(this.fireEvent('crop', this, this.cropData) !== false){
35821             this.process(this.file, this.cropData);
35822         }
35823         
35824         return;
35825         
35826     },
35827     
35828     setThumbBoxSize : function()
35829     {
35830         var width, height;
35831         
35832         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
35833             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
35834             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
35835             
35836             this.minWidth = width;
35837             this.minHeight = height;
35838             
35839             if(this.rotate == 90 || this.rotate == 270){
35840                 this.minWidth = height;
35841                 this.minHeight = width;
35842             }
35843         }
35844         
35845         height = 300;
35846         width = Math.ceil(this.minWidth * height / this.minHeight);
35847         
35848         if(this.minWidth > this.minHeight){
35849             width = 300;
35850             height = Math.ceil(this.minHeight * width / this.minWidth);
35851         }
35852         
35853         this.thumbEl.setStyle({
35854             width : width + 'px',
35855             height : height + 'px'
35856         });
35857
35858         return;
35859             
35860     },
35861     
35862     setThumbBoxPosition : function()
35863     {
35864         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
35865         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
35866         
35867         this.thumbEl.setLeft(x);
35868         this.thumbEl.setTop(y);
35869         
35870     },
35871     
35872     baseRotateLevel : function()
35873     {
35874         this.baseRotate = 1;
35875         
35876         if(
35877                 typeof(this.exif) != 'undefined' &&
35878                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
35879                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
35880         ){
35881             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
35882         }
35883         
35884         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
35885         
35886     },
35887     
35888     baseScaleLevel : function()
35889     {
35890         var width, height;
35891         
35892         if(this.isDocument){
35893             
35894             if(this.baseRotate == 6 || this.baseRotate == 8){
35895             
35896                 height = this.thumbEl.getHeight();
35897                 this.baseScale = height / this.imageEl.OriginWidth;
35898
35899                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
35900                     width = this.thumbEl.getWidth();
35901                     this.baseScale = width / this.imageEl.OriginHeight;
35902                 }
35903
35904                 return;
35905             }
35906
35907             height = this.thumbEl.getHeight();
35908             this.baseScale = height / this.imageEl.OriginHeight;
35909
35910             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
35911                 width = this.thumbEl.getWidth();
35912                 this.baseScale = width / this.imageEl.OriginWidth;
35913             }
35914
35915             return;
35916         }
35917         
35918         if(this.baseRotate == 6 || this.baseRotate == 8){
35919             
35920             width = this.thumbEl.getHeight();
35921             this.baseScale = width / this.imageEl.OriginHeight;
35922             
35923             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
35924                 height = this.thumbEl.getWidth();
35925                 this.baseScale = height / this.imageEl.OriginHeight;
35926             }
35927             
35928             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35929                 height = this.thumbEl.getWidth();
35930                 this.baseScale = height / this.imageEl.OriginHeight;
35931                 
35932                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
35933                     width = this.thumbEl.getHeight();
35934                     this.baseScale = width / this.imageEl.OriginWidth;
35935                 }
35936             }
35937             
35938             return;
35939         }
35940         
35941         width = this.thumbEl.getWidth();
35942         this.baseScale = width / this.imageEl.OriginWidth;
35943         
35944         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
35945             height = this.thumbEl.getHeight();
35946             this.baseScale = height / this.imageEl.OriginHeight;
35947         }
35948         
35949         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35950             
35951             height = this.thumbEl.getHeight();
35952             this.baseScale = height / this.imageEl.OriginHeight;
35953             
35954             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
35955                 width = this.thumbEl.getWidth();
35956                 this.baseScale = width / this.imageEl.OriginWidth;
35957             }
35958             
35959         }
35960         
35961         return;
35962     },
35963     
35964     getScaleLevel : function()
35965     {
35966         return this.baseScale * Math.pow(1.1, this.scale);
35967     },
35968     
35969     onTouchStart : function(e)
35970     {
35971         if(!this.canvasLoaded){
35972             this.beforeSelectFile(e);
35973             return;
35974         }
35975         
35976         var touches = e.browserEvent.touches;
35977         
35978         if(!touches){
35979             return;
35980         }
35981         
35982         if(touches.length == 1){
35983             this.onMouseDown(e);
35984             return;
35985         }
35986         
35987         if(touches.length != 2){
35988             return;
35989         }
35990         
35991         var coords = [];
35992         
35993         for(var i = 0, finger; finger = touches[i]; i++){
35994             coords.push(finger.pageX, finger.pageY);
35995         }
35996         
35997         var x = Math.pow(coords[0] - coords[2], 2);
35998         var y = Math.pow(coords[1] - coords[3], 2);
35999         
36000         this.startDistance = Math.sqrt(x + y);
36001         
36002         this.startScale = this.scale;
36003         
36004         this.pinching = true;
36005         this.dragable = false;
36006         
36007     },
36008     
36009     onTouchMove : function(e)
36010     {
36011         if(!this.pinching && !this.dragable){
36012             return;
36013         }
36014         
36015         var touches = e.browserEvent.touches;
36016         
36017         if(!touches){
36018             return;
36019         }
36020         
36021         if(this.dragable){
36022             this.onMouseMove(e);
36023             return;
36024         }
36025         
36026         var coords = [];
36027         
36028         for(var i = 0, finger; finger = touches[i]; i++){
36029             coords.push(finger.pageX, finger.pageY);
36030         }
36031         
36032         var x = Math.pow(coords[0] - coords[2], 2);
36033         var y = Math.pow(coords[1] - coords[3], 2);
36034         
36035         this.endDistance = Math.sqrt(x + y);
36036         
36037         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36038         
36039         if(!this.zoomable()){
36040             this.scale = this.startScale;
36041             return;
36042         }
36043         
36044         this.draw();
36045         
36046     },
36047     
36048     onTouchEnd : function(e)
36049     {
36050         this.pinching = false;
36051         this.dragable = false;
36052         
36053     },
36054     
36055     process : function(file, crop)
36056     {
36057         if(this.loadMask){
36058             this.maskEl.mask(this.loadingText);
36059         }
36060         
36061         this.xhr = new XMLHttpRequest();
36062         
36063         file.xhr = this.xhr;
36064
36065         this.xhr.open(this.method, this.url, true);
36066         
36067         var headers = {
36068             "Accept": "application/json",
36069             "Cache-Control": "no-cache",
36070             "X-Requested-With": "XMLHttpRequest"
36071         };
36072         
36073         for (var headerName in headers) {
36074             var headerValue = headers[headerName];
36075             if (headerValue) {
36076                 this.xhr.setRequestHeader(headerName, headerValue);
36077             }
36078         }
36079         
36080         var _this = this;
36081         
36082         this.xhr.onload = function()
36083         {
36084             _this.xhrOnLoad(_this.xhr);
36085         }
36086         
36087         this.xhr.onerror = function()
36088         {
36089             _this.xhrOnError(_this.xhr);
36090         }
36091         
36092         var formData = new FormData();
36093
36094         formData.append('returnHTML', 'NO');
36095         
36096         if(crop){
36097             formData.append('crop', crop);
36098         }
36099         
36100         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36101             formData.append(this.paramName, file, file.name);
36102         }
36103         
36104         if(typeof(file.filename) != 'undefined'){
36105             formData.append('filename', file.filename);
36106         }
36107         
36108         if(typeof(file.mimetype) != 'undefined'){
36109             formData.append('mimetype', file.mimetype);
36110         }
36111         
36112         if(this.fireEvent('arrange', this, formData) != false){
36113             this.xhr.send(formData);
36114         };
36115     },
36116     
36117     xhrOnLoad : function(xhr)
36118     {
36119         if(this.loadMask){
36120             this.maskEl.unmask();
36121         }
36122         
36123         if (xhr.readyState !== 4) {
36124             this.fireEvent('exception', this, xhr);
36125             return;
36126         }
36127
36128         var response = Roo.decode(xhr.responseText);
36129         
36130         if(!response.success){
36131             this.fireEvent('exception', this, xhr);
36132             return;
36133         }
36134         
36135         var response = Roo.decode(xhr.responseText);
36136         
36137         this.fireEvent('upload', this, response);
36138         
36139     },
36140     
36141     xhrOnError : function()
36142     {
36143         if(this.loadMask){
36144             this.maskEl.unmask();
36145         }
36146         
36147         Roo.log('xhr on error');
36148         
36149         var response = Roo.decode(xhr.responseText);
36150           
36151         Roo.log(response);
36152         
36153     },
36154     
36155     prepare : function(file)
36156     {   
36157         if(this.loadMask){
36158             this.maskEl.mask(this.loadingText);
36159         }
36160         
36161         this.file = false;
36162         this.exif = {};
36163         
36164         if(typeof(file) === 'string'){
36165             this.loadCanvas(file);
36166             return;
36167         }
36168         
36169         if(!file || !this.urlAPI){
36170             return;
36171         }
36172         
36173         this.file = file;
36174         this.cropType = file.type;
36175         
36176         var _this = this;
36177         
36178         if(this.fireEvent('prepare', this, this.file) != false){
36179             
36180             var reader = new FileReader();
36181             
36182             reader.onload = function (e) {
36183                 if (e.target.error) {
36184                     Roo.log(e.target.error);
36185                     return;
36186                 }
36187                 
36188                 var buffer = e.target.result,
36189                     dataView = new DataView(buffer),
36190                     offset = 2,
36191                     maxOffset = dataView.byteLength - 4,
36192                     markerBytes,
36193                     markerLength;
36194                 
36195                 if (dataView.getUint16(0) === 0xffd8) {
36196                     while (offset < maxOffset) {
36197                         markerBytes = dataView.getUint16(offset);
36198                         
36199                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36200                             markerLength = dataView.getUint16(offset + 2) + 2;
36201                             if (offset + markerLength > dataView.byteLength) {
36202                                 Roo.log('Invalid meta data: Invalid segment size.');
36203                                 break;
36204                             }
36205                             
36206                             if(markerBytes == 0xffe1){
36207                                 _this.parseExifData(
36208                                     dataView,
36209                                     offset,
36210                                     markerLength
36211                                 );
36212                             }
36213                             
36214                             offset += markerLength;
36215                             
36216                             continue;
36217                         }
36218                         
36219                         break;
36220                     }
36221                     
36222                 }
36223                 
36224                 var url = _this.urlAPI.createObjectURL(_this.file);
36225                 
36226                 _this.loadCanvas(url);
36227                 
36228                 return;
36229             }
36230             
36231             reader.readAsArrayBuffer(this.file);
36232             
36233         }
36234         
36235     },
36236     
36237     parseExifData : function(dataView, offset, length)
36238     {
36239         var tiffOffset = offset + 10,
36240             littleEndian,
36241             dirOffset;
36242     
36243         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36244             // No Exif data, might be XMP data instead
36245             return;
36246         }
36247         
36248         // Check for the ASCII code for "Exif" (0x45786966):
36249         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36250             // No Exif data, might be XMP data instead
36251             return;
36252         }
36253         if (tiffOffset + 8 > dataView.byteLength) {
36254             Roo.log('Invalid Exif data: Invalid segment size.');
36255             return;
36256         }
36257         // Check for the two null bytes:
36258         if (dataView.getUint16(offset + 8) !== 0x0000) {
36259             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36260             return;
36261         }
36262         // Check the byte alignment:
36263         switch (dataView.getUint16(tiffOffset)) {
36264         case 0x4949:
36265             littleEndian = true;
36266             break;
36267         case 0x4D4D:
36268             littleEndian = false;
36269             break;
36270         default:
36271             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36272             return;
36273         }
36274         // Check for the TIFF tag marker (0x002A):
36275         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36276             Roo.log('Invalid Exif data: Missing TIFF marker.');
36277             return;
36278         }
36279         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36280         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36281         
36282         this.parseExifTags(
36283             dataView,
36284             tiffOffset,
36285             tiffOffset + dirOffset,
36286             littleEndian
36287         );
36288     },
36289     
36290     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36291     {
36292         var tagsNumber,
36293             dirEndOffset,
36294             i;
36295         if (dirOffset + 6 > dataView.byteLength) {
36296             Roo.log('Invalid Exif data: Invalid directory offset.');
36297             return;
36298         }
36299         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36300         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36301         if (dirEndOffset + 4 > dataView.byteLength) {
36302             Roo.log('Invalid Exif data: Invalid directory size.');
36303             return;
36304         }
36305         for (i = 0; i < tagsNumber; i += 1) {
36306             this.parseExifTag(
36307                 dataView,
36308                 tiffOffset,
36309                 dirOffset + 2 + 12 * i, // tag offset
36310                 littleEndian
36311             );
36312         }
36313         // Return the offset to the next directory:
36314         return dataView.getUint32(dirEndOffset, littleEndian);
36315     },
36316     
36317     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36318     {
36319         var tag = dataView.getUint16(offset, littleEndian);
36320         
36321         this.exif[tag] = this.getExifValue(
36322             dataView,
36323             tiffOffset,
36324             offset,
36325             dataView.getUint16(offset + 2, littleEndian), // tag type
36326             dataView.getUint32(offset + 4, littleEndian), // tag length
36327             littleEndian
36328         );
36329     },
36330     
36331     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36332     {
36333         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36334             tagSize,
36335             dataOffset,
36336             values,
36337             i,
36338             str,
36339             c;
36340     
36341         if (!tagType) {
36342             Roo.log('Invalid Exif data: Invalid tag type.');
36343             return;
36344         }
36345         
36346         tagSize = tagType.size * length;
36347         // Determine if the value is contained in the dataOffset bytes,
36348         // or if the value at the dataOffset is a pointer to the actual data:
36349         dataOffset = tagSize > 4 ?
36350                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36351         if (dataOffset + tagSize > dataView.byteLength) {
36352             Roo.log('Invalid Exif data: Invalid data offset.');
36353             return;
36354         }
36355         if (length === 1) {
36356             return tagType.getValue(dataView, dataOffset, littleEndian);
36357         }
36358         values = [];
36359         for (i = 0; i < length; i += 1) {
36360             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36361         }
36362         
36363         if (tagType.ascii) {
36364             str = '';
36365             // Concatenate the chars:
36366             for (i = 0; i < values.length; i += 1) {
36367                 c = values[i];
36368                 // Ignore the terminating NULL byte(s):
36369                 if (c === '\u0000') {
36370                     break;
36371                 }
36372                 str += c;
36373             }
36374             return str;
36375         }
36376         return values;
36377     }
36378     
36379 });
36380
36381 Roo.apply(Roo.bootstrap.UploadCropbox, {
36382     tags : {
36383         'Orientation': 0x0112
36384     },
36385     
36386     Orientation: {
36387             1: 0, //'top-left',
36388 //            2: 'top-right',
36389             3: 180, //'bottom-right',
36390 //            4: 'bottom-left',
36391 //            5: 'left-top',
36392             6: 90, //'right-top',
36393 //            7: 'right-bottom',
36394             8: 270 //'left-bottom'
36395     },
36396     
36397     exifTagTypes : {
36398         // byte, 8-bit unsigned int:
36399         1: {
36400             getValue: function (dataView, dataOffset) {
36401                 return dataView.getUint8(dataOffset);
36402             },
36403             size: 1
36404         },
36405         // ascii, 8-bit byte:
36406         2: {
36407             getValue: function (dataView, dataOffset) {
36408                 return String.fromCharCode(dataView.getUint8(dataOffset));
36409             },
36410             size: 1,
36411             ascii: true
36412         },
36413         // short, 16 bit int:
36414         3: {
36415             getValue: function (dataView, dataOffset, littleEndian) {
36416                 return dataView.getUint16(dataOffset, littleEndian);
36417             },
36418             size: 2
36419         },
36420         // long, 32 bit int:
36421         4: {
36422             getValue: function (dataView, dataOffset, littleEndian) {
36423                 return dataView.getUint32(dataOffset, littleEndian);
36424             },
36425             size: 4
36426         },
36427         // rational = two long values, first is numerator, second is denominator:
36428         5: {
36429             getValue: function (dataView, dataOffset, littleEndian) {
36430                 return dataView.getUint32(dataOffset, littleEndian) /
36431                     dataView.getUint32(dataOffset + 4, littleEndian);
36432             },
36433             size: 8
36434         },
36435         // slong, 32 bit signed int:
36436         9: {
36437             getValue: function (dataView, dataOffset, littleEndian) {
36438                 return dataView.getInt32(dataOffset, littleEndian);
36439             },
36440             size: 4
36441         },
36442         // srational, two slongs, first is numerator, second is denominator:
36443         10: {
36444             getValue: function (dataView, dataOffset, littleEndian) {
36445                 return dataView.getInt32(dataOffset, littleEndian) /
36446                     dataView.getInt32(dataOffset + 4, littleEndian);
36447             },
36448             size: 8
36449         }
36450     },
36451     
36452     footer : {
36453         STANDARD : [
36454             {
36455                 tag : 'div',
36456                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36457                 action : 'rotate-left',
36458                 cn : [
36459                     {
36460                         tag : 'button',
36461                         cls : 'btn btn-default',
36462                         html : '<i class="fa fa-undo"></i>'
36463                     }
36464                 ]
36465             },
36466             {
36467                 tag : 'div',
36468                 cls : 'btn-group roo-upload-cropbox-picture',
36469                 action : 'picture',
36470                 cn : [
36471                     {
36472                         tag : 'button',
36473                         cls : 'btn btn-default',
36474                         html : '<i class="fa fa-picture-o"></i>'
36475                     }
36476                 ]
36477             },
36478             {
36479                 tag : 'div',
36480                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36481                 action : 'rotate-right',
36482                 cn : [
36483                     {
36484                         tag : 'button',
36485                         cls : 'btn btn-default',
36486                         html : '<i class="fa fa-repeat"></i>'
36487                     }
36488                 ]
36489             }
36490         ],
36491         DOCUMENT : [
36492             {
36493                 tag : 'div',
36494                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36495                 action : 'rotate-left',
36496                 cn : [
36497                     {
36498                         tag : 'button',
36499                         cls : 'btn btn-default',
36500                         html : '<i class="fa fa-undo"></i>'
36501                     }
36502                 ]
36503             },
36504             {
36505                 tag : 'div',
36506                 cls : 'btn-group roo-upload-cropbox-download',
36507                 action : 'download',
36508                 cn : [
36509                     {
36510                         tag : 'button',
36511                         cls : 'btn btn-default',
36512                         html : '<i class="fa fa-download"></i>'
36513                     }
36514                 ]
36515             },
36516             {
36517                 tag : 'div',
36518                 cls : 'btn-group roo-upload-cropbox-crop',
36519                 action : 'crop',
36520                 cn : [
36521                     {
36522                         tag : 'button',
36523                         cls : 'btn btn-default',
36524                         html : '<i class="fa fa-crop"></i>'
36525                     }
36526                 ]
36527             },
36528             {
36529                 tag : 'div',
36530                 cls : 'btn-group roo-upload-cropbox-trash',
36531                 action : 'trash',
36532                 cn : [
36533                     {
36534                         tag : 'button',
36535                         cls : 'btn btn-default',
36536                         html : '<i class="fa fa-trash"></i>'
36537                     }
36538                 ]
36539             },
36540             {
36541                 tag : 'div',
36542                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36543                 action : 'rotate-right',
36544                 cn : [
36545                     {
36546                         tag : 'button',
36547                         cls : 'btn btn-default',
36548                         html : '<i class="fa fa-repeat"></i>'
36549                     }
36550                 ]
36551             }
36552         ],
36553         ROTATOR : [
36554             {
36555                 tag : 'div',
36556                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36557                 action : 'rotate-left',
36558                 cn : [
36559                     {
36560                         tag : 'button',
36561                         cls : 'btn btn-default',
36562                         html : '<i class="fa fa-undo"></i>'
36563                     }
36564                 ]
36565             },
36566             {
36567                 tag : 'div',
36568                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36569                 action : 'rotate-right',
36570                 cn : [
36571                     {
36572                         tag : 'button',
36573                         cls : 'btn btn-default',
36574                         html : '<i class="fa fa-repeat"></i>'
36575                     }
36576                 ]
36577             }
36578         ]
36579     }
36580 });
36581
36582 /*
36583 * Licence: LGPL
36584 */
36585
36586 /**
36587  * @class Roo.bootstrap.DocumentManager
36588  * @extends Roo.bootstrap.Component
36589  * Bootstrap DocumentManager class
36590  * @cfg {String} paramName default 'imageUpload'
36591  * @cfg {String} toolTipName default 'filename'
36592  * @cfg {String} method default POST
36593  * @cfg {String} url action url
36594  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
36595  * @cfg {Boolean} multiple multiple upload default true
36596  * @cfg {Number} thumbSize default 300
36597  * @cfg {String} fieldLabel
36598  * @cfg {Number} labelWidth default 4
36599  * @cfg {String} labelAlign (left|top) default left
36600  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
36601 * @cfg {Number} labellg set the width of label (1-12)
36602  * @cfg {Number} labelmd set the width of label (1-12)
36603  * @cfg {Number} labelsm set the width of label (1-12)
36604  * @cfg {Number} labelxs set the width of label (1-12)
36605  * 
36606  * @constructor
36607  * Create a new DocumentManager
36608  * @param {Object} config The config object
36609  */
36610
36611 Roo.bootstrap.DocumentManager = function(config){
36612     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
36613     
36614     this.files = [];
36615     this.delegates = [];
36616     
36617     this.addEvents({
36618         /**
36619          * @event initial
36620          * Fire when initial the DocumentManager
36621          * @param {Roo.bootstrap.DocumentManager} this
36622          */
36623         "initial" : true,
36624         /**
36625          * @event inspect
36626          * inspect selected file
36627          * @param {Roo.bootstrap.DocumentManager} this
36628          * @param {File} file
36629          */
36630         "inspect" : true,
36631         /**
36632          * @event exception
36633          * Fire when xhr load exception
36634          * @param {Roo.bootstrap.DocumentManager} this
36635          * @param {XMLHttpRequest} xhr
36636          */
36637         "exception" : true,
36638         /**
36639          * @event afterupload
36640          * Fire when xhr load exception
36641          * @param {Roo.bootstrap.DocumentManager} this
36642          * @param {XMLHttpRequest} xhr
36643          */
36644         "afterupload" : true,
36645         /**
36646          * @event prepare
36647          * prepare the form data
36648          * @param {Roo.bootstrap.DocumentManager} this
36649          * @param {Object} formData
36650          */
36651         "prepare" : true,
36652         /**
36653          * @event remove
36654          * Fire when remove the file
36655          * @param {Roo.bootstrap.DocumentManager} this
36656          * @param {Object} file
36657          */
36658         "remove" : true,
36659         /**
36660          * @event refresh
36661          * Fire after refresh the file
36662          * @param {Roo.bootstrap.DocumentManager} this
36663          */
36664         "refresh" : true,
36665         /**
36666          * @event click
36667          * Fire after click the image
36668          * @param {Roo.bootstrap.DocumentManager} this
36669          * @param {Object} file
36670          */
36671         "click" : true,
36672         /**
36673          * @event edit
36674          * Fire when upload a image and editable set to true
36675          * @param {Roo.bootstrap.DocumentManager} this
36676          * @param {Object} file
36677          */
36678         "edit" : true,
36679         /**
36680          * @event beforeselectfile
36681          * Fire before select file
36682          * @param {Roo.bootstrap.DocumentManager} this
36683          */
36684         "beforeselectfile" : true,
36685         /**
36686          * @event process
36687          * Fire before process file
36688          * @param {Roo.bootstrap.DocumentManager} this
36689          * @param {Object} file
36690          */
36691         "process" : true,
36692         /**
36693          * @event previewrendered
36694          * Fire when preview rendered
36695          * @param {Roo.bootstrap.DocumentManager} this
36696          * @param {Object} file
36697          */
36698         "previewrendered" : true,
36699         /**
36700          */
36701         "previewResize" : true
36702         
36703     });
36704 };
36705
36706 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
36707     
36708     boxes : 0,
36709     inputName : '',
36710     thumbSize : 300,
36711     multiple : true,
36712     files : false,
36713     method : 'POST',
36714     url : '',
36715     paramName : 'imageUpload',
36716     toolTipName : 'filename',
36717     fieldLabel : '',
36718     labelWidth : 4,
36719     labelAlign : 'left',
36720     editable : true,
36721     delegates : false,
36722     xhr : false, 
36723     
36724     labellg : 0,
36725     labelmd : 0,
36726     labelsm : 0,
36727     labelxs : 0,
36728     
36729     getAutoCreate : function()
36730     {   
36731         var managerWidget = {
36732             tag : 'div',
36733             cls : 'roo-document-manager',
36734             cn : [
36735                 {
36736                     tag : 'input',
36737                     cls : 'roo-document-manager-selector',
36738                     type : 'file'
36739                 },
36740                 {
36741                     tag : 'div',
36742                     cls : 'roo-document-manager-uploader',
36743                     cn : [
36744                         {
36745                             tag : 'div',
36746                             cls : 'roo-document-manager-upload-btn',
36747                             html : '<i class="fa fa-plus"></i>'
36748                         }
36749                     ]
36750                     
36751                 }
36752             ]
36753         };
36754         
36755         var content = [
36756             {
36757                 tag : 'div',
36758                 cls : 'column col-md-12',
36759                 cn : managerWidget
36760             }
36761         ];
36762         
36763         if(this.fieldLabel.length){
36764             
36765             content = [
36766                 {
36767                     tag : 'div',
36768                     cls : 'column col-md-12',
36769                     html : this.fieldLabel
36770                 },
36771                 {
36772                     tag : 'div',
36773                     cls : 'column col-md-12',
36774                     cn : managerWidget
36775                 }
36776             ];
36777
36778             if(this.labelAlign == 'left'){
36779                 content = [
36780                     {
36781                         tag : 'div',
36782                         cls : 'column',
36783                         html : this.fieldLabel
36784                     },
36785                     {
36786                         tag : 'div',
36787                         cls : 'column',
36788                         cn : managerWidget
36789                     }
36790                 ];
36791                 
36792                 if(this.labelWidth > 12){
36793                     content[0].style = "width: " + this.labelWidth + 'px';
36794                 }
36795
36796                 if(this.labelWidth < 13 && this.labelmd == 0){
36797                     this.labelmd = this.labelWidth;
36798                 }
36799
36800                 if(this.labellg > 0){
36801                     content[0].cls += ' col-lg-' + this.labellg;
36802                     content[1].cls += ' col-lg-' + (12 - this.labellg);
36803                 }
36804
36805                 if(this.labelmd > 0){
36806                     content[0].cls += ' col-md-' + this.labelmd;
36807                     content[1].cls += ' col-md-' + (12 - this.labelmd);
36808                 }
36809
36810                 if(this.labelsm > 0){
36811                     content[0].cls += ' col-sm-' + this.labelsm;
36812                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
36813                 }
36814
36815                 if(this.labelxs > 0){
36816                     content[0].cls += ' col-xs-' + this.labelxs;
36817                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
36818                 }
36819                 
36820             }
36821         }
36822         
36823         var cfg = {
36824             tag : 'div',
36825             cls : 'row clearfix',
36826             cn : content
36827         };
36828         
36829         return cfg;
36830         
36831     },
36832     
36833     initEvents : function()
36834     {
36835         this.managerEl = this.el.select('.roo-document-manager', true).first();
36836         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36837         
36838         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
36839         this.selectorEl.hide();
36840         
36841         if(this.multiple){
36842             this.selectorEl.attr('multiple', 'multiple');
36843         }
36844         
36845         this.selectorEl.on('change', this.onFileSelected, this);
36846         
36847         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
36848         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36849         
36850         this.uploader.on('click', this.onUploaderClick, this);
36851         
36852         this.renderProgressDialog();
36853         
36854         var _this = this;
36855         
36856         window.addEventListener("resize", function() { _this.refresh(); } );
36857         
36858         this.fireEvent('initial', this);
36859     },
36860     
36861     renderProgressDialog : function()
36862     {
36863         var _this = this;
36864         
36865         this.progressDialog = new Roo.bootstrap.Modal({
36866             cls : 'roo-document-manager-progress-dialog',
36867             allow_close : false,
36868             animate : false,
36869             title : '',
36870             buttons : [
36871                 {
36872                     name  :'cancel',
36873                     weight : 'danger',
36874                     html : 'Cancel'
36875                 }
36876             ], 
36877             listeners : { 
36878                 btnclick : function() {
36879                     _this.uploadCancel();
36880                     this.hide();
36881                 }
36882             }
36883         });
36884          
36885         this.progressDialog.render(Roo.get(document.body));
36886          
36887         this.progress = new Roo.bootstrap.Progress({
36888             cls : 'roo-document-manager-progress',
36889             active : true,
36890             striped : true
36891         });
36892         
36893         this.progress.render(this.progressDialog.getChildContainer());
36894         
36895         this.progressBar = new Roo.bootstrap.ProgressBar({
36896             cls : 'roo-document-manager-progress-bar',
36897             aria_valuenow : 0,
36898             aria_valuemin : 0,
36899             aria_valuemax : 12,
36900             panel : 'success'
36901         });
36902         
36903         this.progressBar.render(this.progress.getChildContainer());
36904     },
36905     
36906     onUploaderClick : function(e)
36907     {
36908         e.preventDefault();
36909      
36910         if(this.fireEvent('beforeselectfile', this) != false){
36911             this.selectorEl.dom.click();
36912         }
36913         
36914     },
36915     
36916     onFileSelected : function(e)
36917     {
36918         e.preventDefault();
36919         
36920         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36921             return;
36922         }
36923         
36924         Roo.each(this.selectorEl.dom.files, function(file){
36925             if(this.fireEvent('inspect', this, file) != false){
36926                 this.files.push(file);
36927             }
36928         }, this);
36929         
36930         this.queue();
36931         
36932     },
36933     
36934     queue : function()
36935     {
36936         this.selectorEl.dom.value = '';
36937         
36938         if(!this.files || !this.files.length){
36939             return;
36940         }
36941         
36942         if(this.boxes > 0 && this.files.length > this.boxes){
36943             this.files = this.files.slice(0, this.boxes);
36944         }
36945         
36946         this.uploader.show();
36947         
36948         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36949             this.uploader.hide();
36950         }
36951         
36952         var _this = this;
36953         
36954         var files = [];
36955         
36956         var docs = [];
36957         
36958         Roo.each(this.files, function(file){
36959             
36960             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36961                 var f = this.renderPreview(file);
36962                 files.push(f);
36963                 return;
36964             }
36965             
36966             if(file.type.indexOf('image') != -1){
36967                 this.delegates.push(
36968                     (function(){
36969                         _this.process(file);
36970                     }).createDelegate(this)
36971                 );
36972         
36973                 return;
36974             }
36975             
36976             docs.push(
36977                 (function(){
36978                     _this.process(file);
36979                 }).createDelegate(this)
36980             );
36981             
36982         }, this);
36983         
36984         this.files = files;
36985         
36986         this.delegates = this.delegates.concat(docs);
36987         
36988         if(!this.delegates.length){
36989             this.refresh();
36990             return;
36991         }
36992         
36993         this.progressBar.aria_valuemax = this.delegates.length;
36994         
36995         this.arrange();
36996         
36997         return;
36998     },
36999     
37000     arrange : function()
37001     {
37002         if(!this.delegates.length){
37003             this.progressDialog.hide();
37004             this.refresh();
37005             return;
37006         }
37007         
37008         var delegate = this.delegates.shift();
37009         
37010         this.progressDialog.show();
37011         
37012         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37013         
37014         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37015         
37016         delegate();
37017     },
37018     
37019     refresh : function()
37020     {
37021         this.uploader.show();
37022         
37023         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37024             this.uploader.hide();
37025         }
37026         
37027         Roo.isTouch ? this.closable(false) : this.closable(true);
37028         
37029         this.fireEvent('refresh', this);
37030     },
37031     
37032     onRemove : function(e, el, o)
37033     {
37034         e.preventDefault();
37035         
37036         this.fireEvent('remove', this, o);
37037         
37038     },
37039     
37040     remove : function(o)
37041     {
37042         var files = [];
37043         
37044         Roo.each(this.files, function(file){
37045             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37046                 files.push(file);
37047                 return;
37048             }
37049
37050             o.target.remove();
37051
37052         }, this);
37053         
37054         this.files = files;
37055         
37056         this.refresh();
37057     },
37058     
37059     clear : function()
37060     {
37061         Roo.each(this.files, function(file){
37062             if(!file.target){
37063                 return;
37064             }
37065             
37066             file.target.remove();
37067
37068         }, this);
37069         
37070         this.files = [];
37071         
37072         this.refresh();
37073     },
37074     
37075     onClick : function(e, el, o)
37076     {
37077         e.preventDefault();
37078         
37079         this.fireEvent('click', this, o);
37080         
37081     },
37082     
37083     closable : function(closable)
37084     {
37085         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37086             
37087             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37088             
37089             if(closable){
37090                 el.show();
37091                 return;
37092             }
37093             
37094             el.hide();
37095             
37096         }, this);
37097     },
37098     
37099     xhrOnLoad : function(xhr)
37100     {
37101         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37102             el.remove();
37103         }, this);
37104         
37105         if (xhr.readyState !== 4) {
37106             this.arrange();
37107             this.fireEvent('exception', this, xhr);
37108             return;
37109         }
37110
37111         var response = Roo.decode(xhr.responseText);
37112         
37113         if(!response.success){
37114             this.arrange();
37115             this.fireEvent('exception', this, xhr);
37116             return;
37117         }
37118         
37119         var file = this.renderPreview(response.data);
37120         
37121         this.files.push(file);
37122         
37123         this.arrange();
37124         
37125         this.fireEvent('afterupload', this, xhr);
37126         
37127     },
37128     
37129     xhrOnError : function(xhr)
37130     {
37131         Roo.log('xhr on error');
37132         
37133         var response = Roo.decode(xhr.responseText);
37134           
37135         Roo.log(response);
37136         
37137         this.arrange();
37138     },
37139     
37140     process : function(file)
37141     {
37142         if(this.fireEvent('process', this, file) !== false){
37143             if(this.editable && file.type.indexOf('image') != -1){
37144                 this.fireEvent('edit', this, file);
37145                 return;
37146             }
37147
37148             this.uploadStart(file, false);
37149
37150             return;
37151         }
37152         
37153     },
37154     
37155     uploadStart : function(file, crop)
37156     {
37157         this.xhr = new XMLHttpRequest();
37158         
37159         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37160             this.arrange();
37161             return;
37162         }
37163         
37164         file.xhr = this.xhr;
37165             
37166         this.managerEl.createChild({
37167             tag : 'div',
37168             cls : 'roo-document-manager-loading',
37169             cn : [
37170                 {
37171                     tag : 'div',
37172                     tooltip : file.name,
37173                     cls : 'roo-document-manager-thumb',
37174                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37175                 }
37176             ]
37177
37178         });
37179
37180         this.xhr.open(this.method, this.url, true);
37181         
37182         var headers = {
37183             "Accept": "application/json",
37184             "Cache-Control": "no-cache",
37185             "X-Requested-With": "XMLHttpRequest"
37186         };
37187         
37188         for (var headerName in headers) {
37189             var headerValue = headers[headerName];
37190             if (headerValue) {
37191                 this.xhr.setRequestHeader(headerName, headerValue);
37192             }
37193         }
37194         
37195         var _this = this;
37196         
37197         this.xhr.onload = function()
37198         {
37199             _this.xhrOnLoad(_this.xhr);
37200         }
37201         
37202         this.xhr.onerror = function()
37203         {
37204             _this.xhrOnError(_this.xhr);
37205         }
37206         
37207         var formData = new FormData();
37208
37209         formData.append('returnHTML', 'NO');
37210         
37211         if(crop){
37212             formData.append('crop', crop);
37213         }
37214         
37215         formData.append(this.paramName, file, file.name);
37216         
37217         var options = {
37218             file : file, 
37219             manually : false
37220         };
37221         
37222         if(this.fireEvent('prepare', this, formData, options) != false){
37223             
37224             if(options.manually){
37225                 return;
37226             }
37227             
37228             this.xhr.send(formData);
37229             return;
37230         };
37231         
37232         this.uploadCancel();
37233     },
37234     
37235     uploadCancel : function()
37236     {
37237         if (this.xhr) {
37238             this.xhr.abort();
37239         }
37240         
37241         this.delegates = [];
37242         
37243         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37244             el.remove();
37245         }, this);
37246         
37247         this.arrange();
37248     },
37249     
37250     renderPreview : function(file)
37251     {
37252         if(typeof(file.target) != 'undefined' && file.target){
37253             return file;
37254         }
37255         
37256         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37257         
37258         var previewEl = this.managerEl.createChild({
37259             tag : 'div',
37260             cls : 'roo-document-manager-preview',
37261             cn : [
37262                 {
37263                     tag : 'div',
37264                     tooltip : file[this.toolTipName],
37265                     cls : 'roo-document-manager-thumb',
37266                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37267                 },
37268                 {
37269                     tag : 'button',
37270                     cls : 'close',
37271                     html : '<i class="fa fa-times-circle"></i>'
37272                 }
37273             ]
37274         });
37275
37276         var close = previewEl.select('button.close', true).first();
37277
37278         close.on('click', this.onRemove, this, file);
37279
37280         file.target = previewEl;
37281
37282         var image = previewEl.select('img', true).first();
37283         
37284         var _this = this;
37285         
37286         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37287         
37288         image.on('click', this.onClick, this, file);
37289         
37290         this.fireEvent('previewrendered', this, file);
37291         
37292         return file;
37293         
37294     },
37295     
37296     onPreviewLoad : function(file, image)
37297     {
37298         if(typeof(file.target) == 'undefined' || !file.target){
37299             return;
37300         }
37301         
37302         var width = image.dom.naturalWidth || image.dom.width;
37303         var height = image.dom.naturalHeight || image.dom.height;
37304         
37305         if(!this.previewResize) {
37306             return;
37307         }
37308         
37309         if(width > height){
37310             file.target.addClass('wide');
37311             return;
37312         }
37313         
37314         file.target.addClass('tall');
37315         return;
37316         
37317     },
37318     
37319     uploadFromSource : function(file, crop)
37320     {
37321         this.xhr = new XMLHttpRequest();
37322         
37323         this.managerEl.createChild({
37324             tag : 'div',
37325             cls : 'roo-document-manager-loading',
37326             cn : [
37327                 {
37328                     tag : 'div',
37329                     tooltip : file.name,
37330                     cls : 'roo-document-manager-thumb',
37331                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37332                 }
37333             ]
37334
37335         });
37336
37337         this.xhr.open(this.method, this.url, true);
37338         
37339         var headers = {
37340             "Accept": "application/json",
37341             "Cache-Control": "no-cache",
37342             "X-Requested-With": "XMLHttpRequest"
37343         };
37344         
37345         for (var headerName in headers) {
37346             var headerValue = headers[headerName];
37347             if (headerValue) {
37348                 this.xhr.setRequestHeader(headerName, headerValue);
37349             }
37350         }
37351         
37352         var _this = this;
37353         
37354         this.xhr.onload = function()
37355         {
37356             _this.xhrOnLoad(_this.xhr);
37357         }
37358         
37359         this.xhr.onerror = function()
37360         {
37361             _this.xhrOnError(_this.xhr);
37362         }
37363         
37364         var formData = new FormData();
37365
37366         formData.append('returnHTML', 'NO');
37367         
37368         formData.append('crop', crop);
37369         
37370         if(typeof(file.filename) != 'undefined'){
37371             formData.append('filename', file.filename);
37372         }
37373         
37374         if(typeof(file.mimetype) != 'undefined'){
37375             formData.append('mimetype', file.mimetype);
37376         }
37377         
37378         Roo.log(formData);
37379         
37380         if(this.fireEvent('prepare', this, formData) != false){
37381             this.xhr.send(formData);
37382         };
37383     }
37384 });
37385
37386 /*
37387 * Licence: LGPL
37388 */
37389
37390 /**
37391  * @class Roo.bootstrap.DocumentViewer
37392  * @extends Roo.bootstrap.Component
37393  * Bootstrap DocumentViewer class
37394  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37395  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37396  * 
37397  * @constructor
37398  * Create a new DocumentViewer
37399  * @param {Object} config The config object
37400  */
37401
37402 Roo.bootstrap.DocumentViewer = function(config){
37403     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37404     
37405     this.addEvents({
37406         /**
37407          * @event initial
37408          * Fire after initEvent
37409          * @param {Roo.bootstrap.DocumentViewer} this
37410          */
37411         "initial" : true,
37412         /**
37413          * @event click
37414          * Fire after click
37415          * @param {Roo.bootstrap.DocumentViewer} this
37416          */
37417         "click" : true,
37418         /**
37419          * @event download
37420          * Fire after download button
37421          * @param {Roo.bootstrap.DocumentViewer} this
37422          */
37423         "download" : true,
37424         /**
37425          * @event trash
37426          * Fire after trash button
37427          * @param {Roo.bootstrap.DocumentViewer} this
37428          */
37429         "trash" : true
37430         
37431     });
37432 };
37433
37434 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37435     
37436     showDownload : true,
37437     
37438     showTrash : true,
37439     
37440     getAutoCreate : function()
37441     {
37442         var cfg = {
37443             tag : 'div',
37444             cls : 'roo-document-viewer',
37445             cn : [
37446                 {
37447                     tag : 'div',
37448                     cls : 'roo-document-viewer-body',
37449                     cn : [
37450                         {
37451                             tag : 'div',
37452                             cls : 'roo-document-viewer-thumb',
37453                             cn : [
37454                                 {
37455                                     tag : 'img',
37456                                     cls : 'roo-document-viewer-image'
37457                                 }
37458                             ]
37459                         }
37460                     ]
37461                 },
37462                 {
37463                     tag : 'div',
37464                     cls : 'roo-document-viewer-footer',
37465                     cn : {
37466                         tag : 'div',
37467                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37468                         cn : [
37469                             {
37470                                 tag : 'div',
37471                                 cls : 'btn-group roo-document-viewer-download',
37472                                 cn : [
37473                                     {
37474                                         tag : 'button',
37475                                         cls : 'btn btn-default',
37476                                         html : '<i class="fa fa-download"></i>'
37477                                     }
37478                                 ]
37479                             },
37480                             {
37481                                 tag : 'div',
37482                                 cls : 'btn-group roo-document-viewer-trash',
37483                                 cn : [
37484                                     {
37485                                         tag : 'button',
37486                                         cls : 'btn btn-default',
37487                                         html : '<i class="fa fa-trash"></i>'
37488                                     }
37489                                 ]
37490                             }
37491                         ]
37492                     }
37493                 }
37494             ]
37495         };
37496         
37497         return cfg;
37498     },
37499     
37500     initEvents : function()
37501     {
37502         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37503         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37504         
37505         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37506         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37507         
37508         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37509         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37510         
37511         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37512         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37513         
37514         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37515         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37516         
37517         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37518         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37519         
37520         this.bodyEl.on('click', this.onClick, this);
37521         this.downloadBtn.on('click', this.onDownload, this);
37522         this.trashBtn.on('click', this.onTrash, this);
37523         
37524         this.downloadBtn.hide();
37525         this.trashBtn.hide();
37526         
37527         if(this.showDownload){
37528             this.downloadBtn.show();
37529         }
37530         
37531         if(this.showTrash){
37532             this.trashBtn.show();
37533         }
37534         
37535         if(!this.showDownload && !this.showTrash) {
37536             this.footerEl.hide();
37537         }
37538         
37539     },
37540     
37541     initial : function()
37542     {
37543         this.fireEvent('initial', this);
37544         
37545     },
37546     
37547     onClick : function(e)
37548     {
37549         e.preventDefault();
37550         
37551         this.fireEvent('click', this);
37552     },
37553     
37554     onDownload : function(e)
37555     {
37556         e.preventDefault();
37557         
37558         this.fireEvent('download', this);
37559     },
37560     
37561     onTrash : function(e)
37562     {
37563         e.preventDefault();
37564         
37565         this.fireEvent('trash', this);
37566     }
37567     
37568 });
37569 /*
37570  * - LGPL
37571  *
37572  * FieldLabel
37573  * 
37574  */
37575
37576 /**
37577  * @class Roo.bootstrap.form.FieldLabel
37578  * @extends Roo.bootstrap.Component
37579  * Bootstrap FieldLabel class
37580  * @cfg {String} html contents of the element
37581  * @cfg {String} tag tag of the element default label
37582  * @cfg {String} cls class of the element
37583  * @cfg {String} target label target 
37584  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
37585  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
37586  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
37587  * @cfg {String} iconTooltip default "This field is required"
37588  * @cfg {String} indicatorpos (left|right) default left
37589  * 
37590  * @constructor
37591  * Create a new FieldLabel
37592  * @param {Object} config The config object
37593  */
37594
37595 Roo.bootstrap.form.FieldLabel = function(config){
37596     Roo.bootstrap.Element.superclass.constructor.call(this, config);
37597     
37598     this.addEvents({
37599             /**
37600              * @event invalid
37601              * Fires after the field has been marked as invalid.
37602              * @param {Roo.form.FieldLabel} this
37603              * @param {String} msg The validation message
37604              */
37605             invalid : true,
37606             /**
37607              * @event valid
37608              * Fires after the field has been validated with no errors.
37609              * @param {Roo.form.FieldLabel} this
37610              */
37611             valid : true
37612         });
37613 };
37614
37615 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
37616     
37617     tag: 'label',
37618     cls: '',
37619     html: '',
37620     target: '',
37621     allowBlank : true,
37622     invalidClass : 'has-warning',
37623     validClass : 'has-success',
37624     iconTooltip : 'This field is required',
37625     indicatorpos : 'left',
37626     
37627     getAutoCreate : function(){
37628         
37629         var cls = "";
37630         if (!this.allowBlank) {
37631             cls  = "visible";
37632         }
37633         
37634         var cfg = {
37635             tag : this.tag,
37636             cls : 'roo-bootstrap-field-label ' + this.cls,
37637             for : this.target,
37638             cn : [
37639                 {
37640                     tag : 'i',
37641                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
37642                     tooltip : this.iconTooltip
37643                 },
37644                 {
37645                     tag : 'span',
37646                     html : this.html
37647                 }
37648             ] 
37649         };
37650         
37651         if(this.indicatorpos == 'right'){
37652             var cfg = {
37653                 tag : this.tag,
37654                 cls : 'roo-bootstrap-field-label ' + this.cls,
37655                 for : this.target,
37656                 cn : [
37657                     {
37658                         tag : 'span',
37659                         html : this.html
37660                     },
37661                     {
37662                         tag : 'i',
37663                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
37664                         tooltip : this.iconTooltip
37665                     }
37666                 ] 
37667             };
37668         }
37669         
37670         return cfg;
37671     },
37672     
37673     initEvents: function() 
37674     {
37675         Roo.bootstrap.Element.superclass.initEvents.call(this);
37676         
37677         this.indicator = this.indicatorEl();
37678         
37679         if(this.indicator){
37680             this.indicator.removeClass('visible');
37681             this.indicator.addClass('invisible');
37682         }
37683         
37684         Roo.bootstrap.form.FieldLabel.register(this);
37685     },
37686     
37687     indicatorEl : function()
37688     {
37689         var indicator = this.el.select('i.roo-required-indicator',true).first();
37690         
37691         if(!indicator){
37692             return false;
37693         }
37694         
37695         return indicator;
37696         
37697     },
37698     
37699     /**
37700      * Mark this field as valid
37701      */
37702     markValid : function()
37703     {
37704         if(this.indicator){
37705             this.indicator.removeClass('visible');
37706             this.indicator.addClass('invisible');
37707         }
37708         if (Roo.bootstrap.version == 3) {
37709             this.el.removeClass(this.invalidClass);
37710             this.el.addClass(this.validClass);
37711         } else {
37712             this.el.removeClass('is-invalid');
37713             this.el.addClass('is-valid');
37714         }
37715         
37716         
37717         this.fireEvent('valid', this);
37718     },
37719     
37720     /**
37721      * Mark this field as invalid
37722      * @param {String} msg The validation message
37723      */
37724     markInvalid : function(msg)
37725     {
37726         if(this.indicator){
37727             this.indicator.removeClass('invisible');
37728             this.indicator.addClass('visible');
37729         }
37730           if (Roo.bootstrap.version == 3) {
37731             this.el.removeClass(this.validClass);
37732             this.el.addClass(this.invalidClass);
37733         } else {
37734             this.el.removeClass('is-valid');
37735             this.el.addClass('is-invalid');
37736         }
37737         
37738         
37739         this.fireEvent('invalid', this, msg);
37740     }
37741     
37742    
37743 });
37744
37745 Roo.apply(Roo.bootstrap.form.FieldLabel, {
37746     
37747     groups: {},
37748     
37749      /**
37750     * register a FieldLabel Group
37751     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
37752     */
37753     register : function(label)
37754     {
37755         if(this.groups.hasOwnProperty(label.target)){
37756             return;
37757         }
37758      
37759         this.groups[label.target] = label;
37760         
37761     },
37762     /**
37763     * fetch a FieldLabel Group based on the target
37764     * @param {string} target
37765     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
37766     */
37767     get: function(target) {
37768         if (typeof(this.groups[target]) == 'undefined') {
37769             return false;
37770         }
37771         
37772         return this.groups[target] ;
37773     }
37774 });
37775
37776  
37777
37778  /*
37779  * - LGPL
37780  *
37781  * page DateSplitField.
37782  * 
37783  */
37784
37785
37786 /**
37787  * @class Roo.bootstrap.form.DateSplitField
37788  * @extends Roo.bootstrap.Component
37789  * Bootstrap DateSplitField class
37790  * @cfg {string} fieldLabel - the label associated
37791  * @cfg {Number} labelWidth set the width of label (0-12)
37792  * @cfg {String} labelAlign (top|left)
37793  * @cfg {Boolean} dayAllowBlank (true|false) default false
37794  * @cfg {Boolean} monthAllowBlank (true|false) default false
37795  * @cfg {Boolean} yearAllowBlank (true|false) default false
37796  * @cfg {string} dayPlaceholder 
37797  * @cfg {string} monthPlaceholder
37798  * @cfg {string} yearPlaceholder
37799  * @cfg {string} dayFormat default 'd'
37800  * @cfg {string} monthFormat default 'm'
37801  * @cfg {string} yearFormat default 'Y'
37802  * @cfg {Number} labellg set the width of label (1-12)
37803  * @cfg {Number} labelmd set the width of label (1-12)
37804  * @cfg {Number} labelsm set the width of label (1-12)
37805  * @cfg {Number} labelxs set the width of label (1-12)
37806
37807  *     
37808  * @constructor
37809  * Create a new DateSplitField
37810  * @param {Object} config The config object
37811  */
37812
37813 Roo.bootstrap.form.DateSplitField = function(config){
37814     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
37815     
37816     this.addEvents({
37817         // raw events
37818          /**
37819          * @event years
37820          * getting the data of years
37821          * @param {Roo.bootstrap.form.DateSplitField} this
37822          * @param {Object} years
37823          */
37824         "years" : true,
37825         /**
37826          * @event days
37827          * getting the data of days
37828          * @param {Roo.bootstrap.form.DateSplitField} this
37829          * @param {Object} days
37830          */
37831         "days" : true,
37832         /**
37833          * @event invalid
37834          * Fires after the field has been marked as invalid.
37835          * @param {Roo.form.Field} this
37836          * @param {String} msg The validation message
37837          */
37838         invalid : true,
37839        /**
37840          * @event valid
37841          * Fires after the field has been validated with no errors.
37842          * @param {Roo.form.Field} this
37843          */
37844         valid : true
37845     });
37846 };
37847
37848 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
37849     
37850     fieldLabel : '',
37851     labelAlign : 'top',
37852     labelWidth : 3,
37853     dayAllowBlank : false,
37854     monthAllowBlank : false,
37855     yearAllowBlank : false,
37856     dayPlaceholder : '',
37857     monthPlaceholder : '',
37858     yearPlaceholder : '',
37859     dayFormat : 'd',
37860     monthFormat : 'm',
37861     yearFormat : 'Y',
37862     isFormField : true,
37863     labellg : 0,
37864     labelmd : 0,
37865     labelsm : 0,
37866     labelxs : 0,
37867     
37868     getAutoCreate : function()
37869     {
37870         var cfg = {
37871             tag : 'div',
37872             cls : 'row roo-date-split-field-group',
37873             cn : [
37874                 {
37875                     tag : 'input',
37876                     type : 'hidden',
37877                     cls : 'form-hidden-field roo-date-split-field-group-value',
37878                     name : this.name
37879                 }
37880             ]
37881         };
37882         
37883         var labelCls = 'col-md-12';
37884         var contentCls = 'col-md-4';
37885         
37886         if(this.fieldLabel){
37887             
37888             var label = {
37889                 tag : 'div',
37890                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
37891                 cn : [
37892                     {
37893                         tag : 'label',
37894                         html : this.fieldLabel
37895                     }
37896                 ]
37897             };
37898             
37899             if(this.labelAlign == 'left'){
37900             
37901                 if(this.labelWidth > 12){
37902                     label.style = "width: " + this.labelWidth + 'px';
37903                 }
37904
37905                 if(this.labelWidth < 13 && this.labelmd == 0){
37906                     this.labelmd = this.labelWidth;
37907                 }
37908
37909                 if(this.labellg > 0){
37910                     labelCls = ' col-lg-' + this.labellg;
37911                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
37912                 }
37913
37914                 if(this.labelmd > 0){
37915                     labelCls = ' col-md-' + this.labelmd;
37916                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
37917                 }
37918
37919                 if(this.labelsm > 0){
37920                     labelCls = ' col-sm-' + this.labelsm;
37921                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
37922                 }
37923
37924                 if(this.labelxs > 0){
37925                     labelCls = ' col-xs-' + this.labelxs;
37926                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
37927                 }
37928             }
37929             
37930             label.cls += ' ' + labelCls;
37931             
37932             cfg.cn.push(label);
37933         }
37934         
37935         Roo.each(['day', 'month', 'year'], function(t){
37936             cfg.cn.push({
37937                 tag : 'div',
37938                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
37939             });
37940         }, this);
37941         
37942         return cfg;
37943     },
37944     
37945     inputEl: function ()
37946     {
37947         return this.el.select('.roo-date-split-field-group-value', true).first();
37948     },
37949     
37950     onRender : function(ct, position) 
37951     {
37952         var _this = this;
37953         
37954         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
37955         
37956         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
37957         
37958         this.dayField = new Roo.bootstrap.form.ComboBox({
37959             allowBlank : this.dayAllowBlank,
37960             alwaysQuery : true,
37961             displayField : 'value',
37962             editable : false,
37963             fieldLabel : '',
37964             forceSelection : true,
37965             mode : 'local',
37966             placeholder : this.dayPlaceholder,
37967             selectOnFocus : true,
37968             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37969             triggerAction : 'all',
37970             typeAhead : true,
37971             valueField : 'value',
37972             store : new Roo.data.SimpleStore({
37973                 data : (function() {    
37974                     var days = [];
37975                     _this.fireEvent('days', _this, days);
37976                     return days;
37977                 })(),
37978                 fields : [ 'value' ]
37979             }),
37980             listeners : {
37981                 select : function (_self, record, index)
37982                 {
37983                     _this.setValue(_this.getValue());
37984                 }
37985             }
37986         });
37987
37988         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
37989         
37990         this.monthField = new Roo.bootstrap.form.MonthField({
37991             after : '<i class=\"fa fa-calendar\"></i>',
37992             allowBlank : this.monthAllowBlank,
37993             placeholder : this.monthPlaceholder,
37994             readOnly : true,
37995             listeners : {
37996                 render : function (_self)
37997                 {
37998                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
37999                         e.preventDefault();
38000                         _self.focus();
38001                     });
38002                 },
38003                 select : function (_self, oldvalue, newvalue)
38004                 {
38005                     _this.setValue(_this.getValue());
38006                 }
38007             }
38008         });
38009         
38010         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38011         
38012         this.yearField = new Roo.bootstrap.form.ComboBox({
38013             allowBlank : this.yearAllowBlank,
38014             alwaysQuery : true,
38015             displayField : 'value',
38016             editable : false,
38017             fieldLabel : '',
38018             forceSelection : true,
38019             mode : 'local',
38020             placeholder : this.yearPlaceholder,
38021             selectOnFocus : true,
38022             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38023             triggerAction : 'all',
38024             typeAhead : true,
38025             valueField : 'value',
38026             store : new Roo.data.SimpleStore({
38027                 data : (function() {
38028                     var years = [];
38029                     _this.fireEvent('years', _this, years);
38030                     return years;
38031                 })(),
38032                 fields : [ 'value' ]
38033             }),
38034             listeners : {
38035                 select : function (_self, record, index)
38036                 {
38037                     _this.setValue(_this.getValue());
38038                 }
38039             }
38040         });
38041
38042         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38043     },
38044     
38045     setValue : function(v, format)
38046     {
38047         this.inputEl.dom.value = v;
38048         
38049         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38050         
38051         var d = Date.parseDate(v, f);
38052         
38053         if(!d){
38054             this.validate();
38055             return;
38056         }
38057         
38058         this.setDay(d.format(this.dayFormat));
38059         this.setMonth(d.format(this.monthFormat));
38060         this.setYear(d.format(this.yearFormat));
38061         
38062         this.validate();
38063         
38064         return;
38065     },
38066     
38067     setDay : function(v)
38068     {
38069         this.dayField.setValue(v);
38070         this.inputEl.dom.value = this.getValue();
38071         this.validate();
38072         return;
38073     },
38074     
38075     setMonth : function(v)
38076     {
38077         this.monthField.setValue(v, true);
38078         this.inputEl.dom.value = this.getValue();
38079         this.validate();
38080         return;
38081     },
38082     
38083     setYear : function(v)
38084     {
38085         this.yearField.setValue(v);
38086         this.inputEl.dom.value = this.getValue();
38087         this.validate();
38088         return;
38089     },
38090     
38091     getDay : function()
38092     {
38093         return this.dayField.getValue();
38094     },
38095     
38096     getMonth : function()
38097     {
38098         return this.monthField.getValue();
38099     },
38100     
38101     getYear : function()
38102     {
38103         return this.yearField.getValue();
38104     },
38105     
38106     getValue : function()
38107     {
38108         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38109         
38110         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38111         
38112         return date;
38113     },
38114     
38115     reset : function()
38116     {
38117         this.setDay('');
38118         this.setMonth('');
38119         this.setYear('');
38120         this.inputEl.dom.value = '';
38121         this.validate();
38122         return;
38123     },
38124     
38125     validate : function()
38126     {
38127         var d = this.dayField.validate();
38128         var m = this.monthField.validate();
38129         var y = this.yearField.validate();
38130         
38131         var valid = true;
38132         
38133         if(
38134                 (!this.dayAllowBlank && !d) ||
38135                 (!this.monthAllowBlank && !m) ||
38136                 (!this.yearAllowBlank && !y)
38137         ){
38138             valid = false;
38139         }
38140         
38141         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38142             return valid;
38143         }
38144         
38145         if(valid){
38146             this.markValid();
38147             return valid;
38148         }
38149         
38150         this.markInvalid();
38151         
38152         return valid;
38153     },
38154     
38155     markValid : function()
38156     {
38157         
38158         var label = this.el.select('label', true).first();
38159         var icon = this.el.select('i.fa-star', true).first();
38160
38161         if(label && icon){
38162             icon.remove();
38163         }
38164         
38165         this.fireEvent('valid', this);
38166     },
38167     
38168      /**
38169      * Mark this field as invalid
38170      * @param {String} msg The validation message
38171      */
38172     markInvalid : function(msg)
38173     {
38174         
38175         var label = this.el.select('label', true).first();
38176         var icon = this.el.select('i.fa-star', true).first();
38177
38178         if(label && !icon){
38179             this.el.select('.roo-date-split-field-label', true).createChild({
38180                 tag : 'i',
38181                 cls : 'text-danger fa fa-lg fa-star',
38182                 tooltip : 'This field is required',
38183                 style : 'margin-right:5px;'
38184             }, label, true);
38185         }
38186         
38187         this.fireEvent('invalid', this, msg);
38188     },
38189     
38190     clearInvalid : function()
38191     {
38192         var label = this.el.select('label', true).first();
38193         var icon = this.el.select('i.fa-star', true).first();
38194
38195         if(label && icon){
38196             icon.remove();
38197         }
38198         
38199         this.fireEvent('valid', this);
38200     },
38201     
38202     getName: function()
38203     {
38204         return this.name;
38205     }
38206     
38207 });
38208
38209  
38210
38211 /**
38212  * @class Roo.bootstrap.LayoutMasonry
38213  * @extends Roo.bootstrap.Component
38214  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38215  * Bootstrap Layout Masonry class
38216  *
38217  * This is based on 
38218  * http://masonry.desandro.com
38219  *
38220  * The idea is to render all the bricks based on vertical width...
38221  *
38222  * The original code extends 'outlayer' - we might need to use that....
38223
38224  * @constructor
38225  * Create a new Element
38226  * @param {Object} config The config object
38227  */
38228
38229 Roo.bootstrap.LayoutMasonry = function(config){
38230     
38231     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38232     
38233     this.bricks = [];
38234     
38235     Roo.bootstrap.LayoutMasonry.register(this);
38236     
38237     this.addEvents({
38238         // raw events
38239         /**
38240          * @event layout
38241          * Fire after layout the items
38242          * @param {Roo.bootstrap.LayoutMasonry} this
38243          * @param {Roo.EventObject} e
38244          */
38245         "layout" : true
38246     });
38247     
38248 };
38249
38250 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38251     
38252     /**
38253      * @cfg {Boolean} isLayoutInstant = no animation?
38254      */   
38255     isLayoutInstant : false, // needed?
38256    
38257     /**
38258      * @cfg {Number} boxWidth  width of the columns
38259      */   
38260     boxWidth : 450,
38261     
38262       /**
38263      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38264      */   
38265     boxHeight : 0,
38266     
38267     /**
38268      * @cfg {Number} padWidth padding below box..
38269      */   
38270     padWidth : 10, 
38271     
38272     /**
38273      * @cfg {Number} gutter gutter width..
38274      */   
38275     gutter : 10,
38276     
38277      /**
38278      * @cfg {Number} maxCols maximum number of columns
38279      */   
38280     
38281     maxCols: 0,
38282     
38283     /**
38284      * @cfg {Boolean} isAutoInitial defalut true
38285      */   
38286     isAutoInitial : true, 
38287     
38288     containerWidth: 0,
38289     
38290     /**
38291      * @cfg {Boolean} isHorizontal defalut false
38292      */   
38293     isHorizontal : false, 
38294
38295     currentSize : null,
38296     
38297     tag: 'div',
38298     
38299     cls: '',
38300     
38301     bricks: null, //CompositeElement
38302     
38303     cols : 1,
38304     
38305     _isLayoutInited : false,
38306     
38307 //    isAlternative : false, // only use for vertical layout...
38308     
38309     /**
38310      * @cfg {Number} alternativePadWidth padding below box..
38311      */   
38312     alternativePadWidth : 50,
38313     
38314     selectedBrick : [],
38315     
38316     getAutoCreate : function(){
38317         
38318         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38319         
38320         var cfg = {
38321             tag: this.tag,
38322             cls: 'blog-masonary-wrapper ' + this.cls,
38323             cn : {
38324                 cls : 'mas-boxes masonary'
38325             }
38326         };
38327         
38328         return cfg;
38329     },
38330     
38331     getChildContainer: function( )
38332     {
38333         if (this.boxesEl) {
38334             return this.boxesEl;
38335         }
38336         
38337         this.boxesEl = this.el.select('.mas-boxes').first();
38338         
38339         return this.boxesEl;
38340     },
38341     
38342     
38343     initEvents : function()
38344     {
38345         var _this = this;
38346         
38347         if(this.isAutoInitial){
38348             Roo.log('hook children rendered');
38349             this.on('childrenrendered', function() {
38350                 Roo.log('children rendered');
38351                 _this.initial();
38352             } ,this);
38353         }
38354     },
38355     
38356     initial : function()
38357     {
38358         this.selectedBrick = [];
38359         
38360         this.currentSize = this.el.getBox(true);
38361         
38362         Roo.EventManager.onWindowResize(this.resize, this); 
38363
38364         if(!this.isAutoInitial){
38365             this.layout();
38366             return;
38367         }
38368         
38369         this.layout();
38370         
38371         return;
38372         //this.layout.defer(500,this);
38373         
38374     },
38375     
38376     resize : function()
38377     {
38378         var cs = this.el.getBox(true);
38379         
38380         if (
38381                 this.currentSize.width == cs.width && 
38382                 this.currentSize.x == cs.x && 
38383                 this.currentSize.height == cs.height && 
38384                 this.currentSize.y == cs.y 
38385         ) {
38386             Roo.log("no change in with or X or Y");
38387             return;
38388         }
38389         
38390         this.currentSize = cs;
38391         
38392         this.layout();
38393         
38394     },
38395     
38396     layout : function()
38397     {   
38398         this._resetLayout();
38399         
38400         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38401         
38402         this.layoutItems( isInstant );
38403       
38404         this._isLayoutInited = true;
38405         
38406         this.fireEvent('layout', this);
38407         
38408     },
38409     
38410     _resetLayout : function()
38411     {
38412         if(this.isHorizontal){
38413             this.horizontalMeasureColumns();
38414             return;
38415         }
38416         
38417         this.verticalMeasureColumns();
38418         
38419     },
38420     
38421     verticalMeasureColumns : function()
38422     {
38423         this.getContainerWidth();
38424         
38425 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38426 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38427 //            return;
38428 //        }
38429         
38430         var boxWidth = this.boxWidth + this.padWidth;
38431         
38432         if(this.containerWidth < this.boxWidth){
38433             boxWidth = this.containerWidth
38434         }
38435         
38436         var containerWidth = this.containerWidth;
38437         
38438         var cols = Math.floor(containerWidth / boxWidth);
38439         
38440         this.cols = Math.max( cols, 1 );
38441         
38442         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38443         
38444         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38445         
38446         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38447         
38448         this.colWidth = boxWidth + avail - this.padWidth;
38449         
38450         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38451         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38452     },
38453     
38454     horizontalMeasureColumns : function()
38455     {
38456         this.getContainerWidth();
38457         
38458         var boxWidth = this.boxWidth;
38459         
38460         if(this.containerWidth < boxWidth){
38461             boxWidth = this.containerWidth;
38462         }
38463         
38464         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38465         
38466         this.el.setHeight(boxWidth);
38467         
38468     },
38469     
38470     getContainerWidth : function()
38471     {
38472         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38473     },
38474     
38475     layoutItems : function( isInstant )
38476     {
38477         Roo.log(this.bricks);
38478         
38479         var items = Roo.apply([], this.bricks);
38480         
38481         if(this.isHorizontal){
38482             this._horizontalLayoutItems( items , isInstant );
38483             return;
38484         }
38485         
38486 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38487 //            this._verticalAlternativeLayoutItems( items , isInstant );
38488 //            return;
38489 //        }
38490         
38491         this._verticalLayoutItems( items , isInstant );
38492         
38493     },
38494     
38495     _verticalLayoutItems : function ( items , isInstant)
38496     {
38497         if ( !items || !items.length ) {
38498             return;
38499         }
38500         
38501         var standard = [
38502             ['xs', 'xs', 'xs', 'tall'],
38503             ['xs', 'xs', 'tall'],
38504             ['xs', 'xs', 'sm'],
38505             ['xs', 'xs', 'xs'],
38506             ['xs', 'tall'],
38507             ['xs', 'sm'],
38508             ['xs', 'xs'],
38509             ['xs'],
38510             
38511             ['sm', 'xs', 'xs'],
38512             ['sm', 'xs'],
38513             ['sm'],
38514             
38515             ['tall', 'xs', 'xs', 'xs'],
38516             ['tall', 'xs', 'xs'],
38517             ['tall', 'xs'],
38518             ['tall']
38519             
38520         ];
38521         
38522         var queue = [];
38523         
38524         var boxes = [];
38525         
38526         var box = [];
38527         
38528         Roo.each(items, function(item, k){
38529             
38530             switch (item.size) {
38531                 // these layouts take up a full box,
38532                 case 'md' :
38533                 case 'md-left' :
38534                 case 'md-right' :
38535                 case 'wide' :
38536                     
38537                     if(box.length){
38538                         boxes.push(box);
38539                         box = [];
38540                     }
38541                     
38542                     boxes.push([item]);
38543                     
38544                     break;
38545                     
38546                 case 'xs' :
38547                 case 'sm' :
38548                 case 'tall' :
38549                     
38550                     box.push(item);
38551                     
38552                     break;
38553                 default :
38554                     break;
38555                     
38556             }
38557             
38558         }, this);
38559         
38560         if(box.length){
38561             boxes.push(box);
38562             box = [];
38563         }
38564         
38565         var filterPattern = function(box, length)
38566         {
38567             if(!box.length){
38568                 return;
38569             }
38570             
38571             var match = false;
38572             
38573             var pattern = box.slice(0, length);
38574             
38575             var format = [];
38576             
38577             Roo.each(pattern, function(i){
38578                 format.push(i.size);
38579             }, this);
38580             
38581             Roo.each(standard, function(s){
38582                 
38583                 if(String(s) != String(format)){
38584                     return;
38585                 }
38586                 
38587                 match = true;
38588                 return false;
38589                 
38590             }, this);
38591             
38592             if(!match && length == 1){
38593                 return;
38594             }
38595             
38596             if(!match){
38597                 filterPattern(box, length - 1);
38598                 return;
38599             }
38600                 
38601             queue.push(pattern);
38602
38603             box = box.slice(length, box.length);
38604
38605             filterPattern(box, 4);
38606
38607             return;
38608             
38609         }
38610         
38611         Roo.each(boxes, function(box, k){
38612             
38613             if(!box.length){
38614                 return;
38615             }
38616             
38617             if(box.length == 1){
38618                 queue.push(box);
38619                 return;
38620             }
38621             
38622             filterPattern(box, 4);
38623             
38624         }, this);
38625         
38626         this._processVerticalLayoutQueue( queue, isInstant );
38627         
38628     },
38629     
38630 //    _verticalAlternativeLayoutItems : function( items , isInstant )
38631 //    {
38632 //        if ( !items || !items.length ) {
38633 //            return;
38634 //        }
38635 //
38636 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
38637 //        
38638 //    },
38639     
38640     _horizontalLayoutItems : function ( items , isInstant)
38641     {
38642         if ( !items || !items.length || items.length < 3) {
38643             return;
38644         }
38645         
38646         items.reverse();
38647         
38648         var eItems = items.slice(0, 3);
38649         
38650         items = items.slice(3, items.length);
38651         
38652         var standard = [
38653             ['xs', 'xs', 'xs', 'wide'],
38654             ['xs', 'xs', 'wide'],
38655             ['xs', 'xs', 'sm'],
38656             ['xs', 'xs', 'xs'],
38657             ['xs', 'wide'],
38658             ['xs', 'sm'],
38659             ['xs', 'xs'],
38660             ['xs'],
38661             
38662             ['sm', 'xs', 'xs'],
38663             ['sm', 'xs'],
38664             ['sm'],
38665             
38666             ['wide', 'xs', 'xs', 'xs'],
38667             ['wide', 'xs', 'xs'],
38668             ['wide', 'xs'],
38669             ['wide'],
38670             
38671             ['wide-thin']
38672         ];
38673         
38674         var queue = [];
38675         
38676         var boxes = [];
38677         
38678         var box = [];
38679         
38680         Roo.each(items, function(item, k){
38681             
38682             switch (item.size) {
38683                 case 'md' :
38684                 case 'md-left' :
38685                 case 'md-right' :
38686                 case 'tall' :
38687                     
38688                     if(box.length){
38689                         boxes.push(box);
38690                         box = [];
38691                     }
38692                     
38693                     boxes.push([item]);
38694                     
38695                     break;
38696                     
38697                 case 'xs' :
38698                 case 'sm' :
38699                 case 'wide' :
38700                 case 'wide-thin' :
38701                     
38702                     box.push(item);
38703                     
38704                     break;
38705                 default :
38706                     break;
38707                     
38708             }
38709             
38710         }, this);
38711         
38712         if(box.length){
38713             boxes.push(box);
38714             box = [];
38715         }
38716         
38717         var filterPattern = function(box, length)
38718         {
38719             if(!box.length){
38720                 return;
38721             }
38722             
38723             var match = false;
38724             
38725             var pattern = box.slice(0, length);
38726             
38727             var format = [];
38728             
38729             Roo.each(pattern, function(i){
38730                 format.push(i.size);
38731             }, this);
38732             
38733             Roo.each(standard, function(s){
38734                 
38735                 if(String(s) != String(format)){
38736                     return;
38737                 }
38738                 
38739                 match = true;
38740                 return false;
38741                 
38742             }, this);
38743             
38744             if(!match && length == 1){
38745                 return;
38746             }
38747             
38748             if(!match){
38749                 filterPattern(box, length - 1);
38750                 return;
38751             }
38752                 
38753             queue.push(pattern);
38754
38755             box = box.slice(length, box.length);
38756
38757             filterPattern(box, 4);
38758
38759             return;
38760             
38761         }
38762         
38763         Roo.each(boxes, function(box, k){
38764             
38765             if(!box.length){
38766                 return;
38767             }
38768             
38769             if(box.length == 1){
38770                 queue.push(box);
38771                 return;
38772             }
38773             
38774             filterPattern(box, 4);
38775             
38776         }, this);
38777         
38778         
38779         var prune = [];
38780         
38781         var pos = this.el.getBox(true);
38782         
38783         var minX = pos.x;
38784         
38785         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38786         
38787         var hit_end = false;
38788         
38789         Roo.each(queue, function(box){
38790             
38791             if(hit_end){
38792                 
38793                 Roo.each(box, function(b){
38794                 
38795                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38796                     b.el.hide();
38797
38798                 }, this);
38799
38800                 return;
38801             }
38802             
38803             var mx = 0;
38804             
38805             Roo.each(box, function(b){
38806                 
38807                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38808                 b.el.show();
38809
38810                 mx = Math.max(mx, b.x);
38811                 
38812             }, this);
38813             
38814             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
38815             
38816             if(maxX < minX){
38817                 
38818                 Roo.each(box, function(b){
38819                 
38820                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38821                     b.el.hide();
38822                     
38823                 }, this);
38824                 
38825                 hit_end = true;
38826                 
38827                 return;
38828             }
38829             
38830             prune.push(box);
38831             
38832         }, this);
38833         
38834         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
38835     },
38836     
38837     /** Sets position of item in DOM
38838     * @param {Element} item
38839     * @param {Number} x - horizontal position
38840     * @param {Number} y - vertical position
38841     * @param {Boolean} isInstant - disables transitions
38842     */
38843     _processVerticalLayoutQueue : function( queue, isInstant )
38844     {
38845         var pos = this.el.getBox(true);
38846         var x = pos.x;
38847         var y = pos.y;
38848         var maxY = [];
38849         
38850         for (var i = 0; i < this.cols; i++){
38851             maxY[i] = pos.y;
38852         }
38853         
38854         Roo.each(queue, function(box, k){
38855             
38856             var col = k % this.cols;
38857             
38858             Roo.each(box, function(b,kk){
38859                 
38860                 b.el.position('absolute');
38861                 
38862                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38863                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38864                 
38865                 if(b.size == 'md-left' || b.size == 'md-right'){
38866                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38867                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38868                 }
38869                 
38870                 b.el.setWidth(width);
38871                 b.el.setHeight(height);
38872                 // iframe?
38873                 b.el.select('iframe',true).setSize(width,height);
38874                 
38875             }, this);
38876             
38877             for (var i = 0; i < this.cols; i++){
38878                 
38879                 if(maxY[i] < maxY[col]){
38880                     col = i;
38881                     continue;
38882                 }
38883                 
38884                 col = Math.min(col, i);
38885                 
38886             }
38887             
38888             x = pos.x + col * (this.colWidth + this.padWidth);
38889             
38890             y = maxY[col];
38891             
38892             var positions = [];
38893             
38894             switch (box.length){
38895                 case 1 :
38896                     positions = this.getVerticalOneBoxColPositions(x, y, box);
38897                     break;
38898                 case 2 :
38899                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
38900                     break;
38901                 case 3 :
38902                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
38903                     break;
38904                 case 4 :
38905                     positions = this.getVerticalFourBoxColPositions(x, y, box);
38906                     break;
38907                 default :
38908                     break;
38909             }
38910             
38911             Roo.each(box, function(b,kk){
38912                 
38913                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38914                 
38915                 var sz = b.el.getSize();
38916                 
38917                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
38918                 
38919             }, this);
38920             
38921         }, this);
38922         
38923         var mY = 0;
38924         
38925         for (var i = 0; i < this.cols; i++){
38926             mY = Math.max(mY, maxY[i]);
38927         }
38928         
38929         this.el.setHeight(mY - pos.y);
38930         
38931     },
38932     
38933 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
38934 //    {
38935 //        var pos = this.el.getBox(true);
38936 //        var x = pos.x;
38937 //        var y = pos.y;
38938 //        var maxX = pos.right;
38939 //        
38940 //        var maxHeight = 0;
38941 //        
38942 //        Roo.each(items, function(item, k){
38943 //            
38944 //            var c = k % 2;
38945 //            
38946 //            item.el.position('absolute');
38947 //                
38948 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
38949 //
38950 //            item.el.setWidth(width);
38951 //
38952 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
38953 //
38954 //            item.el.setHeight(height);
38955 //            
38956 //            if(c == 0){
38957 //                item.el.setXY([x, y], isInstant ? false : true);
38958 //            } else {
38959 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
38960 //            }
38961 //            
38962 //            y = y + height + this.alternativePadWidth;
38963 //            
38964 //            maxHeight = maxHeight + height + this.alternativePadWidth;
38965 //            
38966 //        }, this);
38967 //        
38968 //        this.el.setHeight(maxHeight);
38969 //        
38970 //    },
38971     
38972     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
38973     {
38974         var pos = this.el.getBox(true);
38975         
38976         var minX = pos.x;
38977         var minY = pos.y;
38978         
38979         var maxX = pos.right;
38980         
38981         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
38982         
38983         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38984         
38985         Roo.each(queue, function(box, k){
38986             
38987             Roo.each(box, function(b, kk){
38988                 
38989                 b.el.position('absolute');
38990                 
38991                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38992                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38993                 
38994                 if(b.size == 'md-left' || b.size == 'md-right'){
38995                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38996                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38997                 }
38998                 
38999                 b.el.setWidth(width);
39000                 b.el.setHeight(height);
39001                 
39002             }, this);
39003             
39004             if(!box.length){
39005                 return;
39006             }
39007             
39008             var positions = [];
39009             
39010             switch (box.length){
39011                 case 1 :
39012                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39013                     break;
39014                 case 2 :
39015                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39016                     break;
39017                 case 3 :
39018                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39019                     break;
39020                 case 4 :
39021                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39022                     break;
39023                 default :
39024                     break;
39025             }
39026             
39027             Roo.each(box, function(b,kk){
39028                 
39029                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39030                 
39031                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39032                 
39033             }, this);
39034             
39035         }, this);
39036         
39037     },
39038     
39039     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39040     {
39041         Roo.each(eItems, function(b,k){
39042             
39043             b.size = (k == 0) ? 'sm' : 'xs';
39044             b.x = (k == 0) ? 2 : 1;
39045             b.y = (k == 0) ? 2 : 1;
39046             
39047             b.el.position('absolute');
39048             
39049             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39050                 
39051             b.el.setWidth(width);
39052             
39053             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39054             
39055             b.el.setHeight(height);
39056             
39057         }, this);
39058
39059         var positions = [];
39060         
39061         positions.push({
39062             x : maxX - this.unitWidth * 2 - this.gutter,
39063             y : minY
39064         });
39065         
39066         positions.push({
39067             x : maxX - this.unitWidth,
39068             y : minY + (this.unitWidth + this.gutter) * 2
39069         });
39070         
39071         positions.push({
39072             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39073             y : minY
39074         });
39075         
39076         Roo.each(eItems, function(b,k){
39077             
39078             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39079
39080         }, this);
39081         
39082     },
39083     
39084     getVerticalOneBoxColPositions : function(x, y, box)
39085     {
39086         var pos = [];
39087         
39088         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39089         
39090         if(box[0].size == 'md-left'){
39091             rand = 0;
39092         }
39093         
39094         if(box[0].size == 'md-right'){
39095             rand = 1;
39096         }
39097         
39098         pos.push({
39099             x : x + (this.unitWidth + this.gutter) * rand,
39100             y : y
39101         });
39102         
39103         return pos;
39104     },
39105     
39106     getVerticalTwoBoxColPositions : function(x, y, box)
39107     {
39108         var pos = [];
39109         
39110         if(box[0].size == 'xs'){
39111             
39112             pos.push({
39113                 x : x,
39114                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39115             });
39116
39117             pos.push({
39118                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39119                 y : y
39120             });
39121             
39122             return pos;
39123             
39124         }
39125         
39126         pos.push({
39127             x : x,
39128             y : y
39129         });
39130
39131         pos.push({
39132             x : x + (this.unitWidth + this.gutter) * 2,
39133             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39134         });
39135         
39136         return pos;
39137         
39138     },
39139     
39140     getVerticalThreeBoxColPositions : function(x, y, box)
39141     {
39142         var pos = [];
39143         
39144         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39145             
39146             pos.push({
39147                 x : x,
39148                 y : y
39149             });
39150
39151             pos.push({
39152                 x : x + (this.unitWidth + this.gutter) * 1,
39153                 y : y
39154             });
39155             
39156             pos.push({
39157                 x : x + (this.unitWidth + this.gutter) * 2,
39158                 y : y
39159             });
39160             
39161             return pos;
39162             
39163         }
39164         
39165         if(box[0].size == 'xs' && box[1].size == 'xs'){
39166             
39167             pos.push({
39168                 x : x,
39169                 y : y
39170             });
39171
39172             pos.push({
39173                 x : x,
39174                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39175             });
39176             
39177             pos.push({
39178                 x : x + (this.unitWidth + this.gutter) * 1,
39179                 y : y
39180             });
39181             
39182             return pos;
39183             
39184         }
39185         
39186         pos.push({
39187             x : x,
39188             y : y
39189         });
39190
39191         pos.push({
39192             x : x + (this.unitWidth + this.gutter) * 2,
39193             y : y
39194         });
39195
39196         pos.push({
39197             x : x + (this.unitWidth + this.gutter) * 2,
39198             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39199         });
39200             
39201         return pos;
39202         
39203     },
39204     
39205     getVerticalFourBoxColPositions : function(x, y, box)
39206     {
39207         var pos = [];
39208         
39209         if(box[0].size == 'xs'){
39210             
39211             pos.push({
39212                 x : x,
39213                 y : y
39214             });
39215
39216             pos.push({
39217                 x : x,
39218                 y : y + (this.unitHeight + this.gutter) * 1
39219             });
39220             
39221             pos.push({
39222                 x : x,
39223                 y : y + (this.unitHeight + this.gutter) * 2
39224             });
39225             
39226             pos.push({
39227                 x : x + (this.unitWidth + this.gutter) * 1,
39228                 y : y
39229             });
39230             
39231             return pos;
39232             
39233         }
39234         
39235         pos.push({
39236             x : x,
39237             y : y
39238         });
39239
39240         pos.push({
39241             x : x + (this.unitWidth + this.gutter) * 2,
39242             y : y
39243         });
39244
39245         pos.push({
39246             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39247             y : y + (this.unitHeight + this.gutter) * 1
39248         });
39249
39250         pos.push({
39251             x : x + (this.unitWidth + this.gutter) * 2,
39252             y : y + (this.unitWidth + this.gutter) * 2
39253         });
39254
39255         return pos;
39256         
39257     },
39258     
39259     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39260     {
39261         var pos = [];
39262         
39263         if(box[0].size == 'md-left'){
39264             pos.push({
39265                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39266                 y : minY
39267             });
39268             
39269             return pos;
39270         }
39271         
39272         if(box[0].size == 'md-right'){
39273             pos.push({
39274                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39275                 y : minY + (this.unitWidth + this.gutter) * 1
39276             });
39277             
39278             return pos;
39279         }
39280         
39281         var rand = Math.floor(Math.random() * (4 - box[0].y));
39282         
39283         pos.push({
39284             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39285             y : minY + (this.unitWidth + this.gutter) * rand
39286         });
39287         
39288         return pos;
39289         
39290     },
39291     
39292     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39293     {
39294         var pos = [];
39295         
39296         if(box[0].size == 'xs'){
39297             
39298             pos.push({
39299                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39300                 y : minY
39301             });
39302
39303             pos.push({
39304                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39305                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39306             });
39307             
39308             return pos;
39309             
39310         }
39311         
39312         pos.push({
39313             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39314             y : minY
39315         });
39316
39317         pos.push({
39318             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39319             y : minY + (this.unitWidth + this.gutter) * 2
39320         });
39321         
39322         return pos;
39323         
39324     },
39325     
39326     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39327     {
39328         var pos = [];
39329         
39330         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39331             
39332             pos.push({
39333                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39334                 y : minY
39335             });
39336
39337             pos.push({
39338                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39339                 y : minY + (this.unitWidth + this.gutter) * 1
39340             });
39341             
39342             pos.push({
39343                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39344                 y : minY + (this.unitWidth + this.gutter) * 2
39345             });
39346             
39347             return pos;
39348             
39349         }
39350         
39351         if(box[0].size == 'xs' && box[1].size == 'xs'){
39352             
39353             pos.push({
39354                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39355                 y : minY
39356             });
39357
39358             pos.push({
39359                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39360                 y : minY
39361             });
39362             
39363             pos.push({
39364                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39365                 y : minY + (this.unitWidth + this.gutter) * 1
39366             });
39367             
39368             return pos;
39369             
39370         }
39371         
39372         pos.push({
39373             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39374             y : minY
39375         });
39376
39377         pos.push({
39378             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39379             y : minY + (this.unitWidth + this.gutter) * 2
39380         });
39381
39382         pos.push({
39383             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39384             y : minY + (this.unitWidth + this.gutter) * 2
39385         });
39386             
39387         return pos;
39388         
39389     },
39390     
39391     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39392     {
39393         var pos = [];
39394         
39395         if(box[0].size == 'xs'){
39396             
39397             pos.push({
39398                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39399                 y : minY
39400             });
39401
39402             pos.push({
39403                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39404                 y : minY
39405             });
39406             
39407             pos.push({
39408                 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),
39409                 y : minY
39410             });
39411             
39412             pos.push({
39413                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39414                 y : minY + (this.unitWidth + this.gutter) * 1
39415             });
39416             
39417             return pos;
39418             
39419         }
39420         
39421         pos.push({
39422             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39423             y : minY
39424         });
39425         
39426         pos.push({
39427             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39428             y : minY + (this.unitWidth + this.gutter) * 2
39429         });
39430         
39431         pos.push({
39432             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39433             y : minY + (this.unitWidth + this.gutter) * 2
39434         });
39435         
39436         pos.push({
39437             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),
39438             y : minY + (this.unitWidth + this.gutter) * 2
39439         });
39440
39441         return pos;
39442         
39443     },
39444     
39445     /**
39446     * remove a Masonry Brick
39447     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39448     */
39449     removeBrick : function(brick_id)
39450     {
39451         if (!brick_id) {
39452             return;
39453         }
39454         
39455         for (var i = 0; i<this.bricks.length; i++) {
39456             if (this.bricks[i].id == brick_id) {
39457                 this.bricks.splice(i,1);
39458                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39459                 this.initial();
39460             }
39461         }
39462     },
39463     
39464     /**
39465     * adds a Masonry Brick
39466     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39467     */
39468     addBrick : function(cfg)
39469     {
39470         var cn = new Roo.bootstrap.MasonryBrick(cfg);
39471         //this.register(cn);
39472         cn.parentId = this.id;
39473         cn.render(this.el);
39474         return cn;
39475     },
39476     
39477     /**
39478     * register a Masonry Brick
39479     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39480     */
39481     
39482     register : function(brick)
39483     {
39484         this.bricks.push(brick);
39485         brick.masonryId = this.id;
39486     },
39487     
39488     /**
39489     * clear all the Masonry Brick
39490     */
39491     clearAll : function()
39492     {
39493         this.bricks = [];
39494         //this.getChildContainer().dom.innerHTML = "";
39495         this.el.dom.innerHTML = '';
39496     },
39497     
39498     getSelected : function()
39499     {
39500         if (!this.selectedBrick) {
39501             return false;
39502         }
39503         
39504         return this.selectedBrick;
39505     }
39506 });
39507
39508 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39509     
39510     groups: {},
39511      /**
39512     * register a Masonry Layout
39513     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39514     */
39515     
39516     register : function(layout)
39517     {
39518         this.groups[layout.id] = layout;
39519     },
39520     /**
39521     * fetch a  Masonry Layout based on the masonry layout ID
39522     * @param {string} the masonry layout to add
39523     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39524     */
39525     
39526     get: function(layout_id) {
39527         if (typeof(this.groups[layout_id]) == 'undefined') {
39528             return false;
39529         }
39530         return this.groups[layout_id] ;
39531     }
39532     
39533     
39534     
39535 });
39536
39537  
39538
39539  /**
39540  *
39541  * This is based on 
39542  * http://masonry.desandro.com
39543  *
39544  * The idea is to render all the bricks based on vertical width...
39545  *
39546  * The original code extends 'outlayer' - we might need to use that....
39547  * 
39548  */
39549
39550
39551 /**
39552  * @class Roo.bootstrap.LayoutMasonryAuto
39553  * @extends Roo.bootstrap.Component
39554  * Bootstrap Layout Masonry class
39555  * 
39556  * @constructor
39557  * Create a new Element
39558  * @param {Object} config The config object
39559  */
39560
39561 Roo.bootstrap.LayoutMasonryAuto = function(config){
39562     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39563 };
39564
39565 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
39566     
39567       /**
39568      * @cfg {Boolean} isFitWidth  - resize the width..
39569      */   
39570     isFitWidth : false,  // options..
39571     /**
39572      * @cfg {Boolean} isOriginLeft = left align?
39573      */   
39574     isOriginLeft : true,
39575     /**
39576      * @cfg {Boolean} isOriginTop = top align?
39577      */   
39578     isOriginTop : false,
39579     /**
39580      * @cfg {Boolean} isLayoutInstant = no animation?
39581      */   
39582     isLayoutInstant : false, // needed?
39583     /**
39584      * @cfg {Boolean} isResizingContainer = not sure if this is used..
39585      */   
39586     isResizingContainer : true,
39587     /**
39588      * @cfg {Number} columnWidth  width of the columns 
39589      */   
39590     
39591     columnWidth : 0,
39592     
39593     /**
39594      * @cfg {Number} maxCols maximum number of columns
39595      */   
39596     
39597     maxCols: 0,
39598     /**
39599      * @cfg {Number} padHeight padding below box..
39600      */   
39601     
39602     padHeight : 10, 
39603     
39604     /**
39605      * @cfg {Boolean} isAutoInitial defalut true
39606      */   
39607     
39608     isAutoInitial : true, 
39609     
39610     // private?
39611     gutter : 0,
39612     
39613     containerWidth: 0,
39614     initialColumnWidth : 0,
39615     currentSize : null,
39616     
39617     colYs : null, // array.
39618     maxY : 0,
39619     padWidth: 10,
39620     
39621     
39622     tag: 'div',
39623     cls: '',
39624     bricks: null, //CompositeElement
39625     cols : 0, // array?
39626     // element : null, // wrapped now this.el
39627     _isLayoutInited : null, 
39628     
39629     
39630     getAutoCreate : function(){
39631         
39632         var cfg = {
39633             tag: this.tag,
39634             cls: 'blog-masonary-wrapper ' + this.cls,
39635             cn : {
39636                 cls : 'mas-boxes masonary'
39637             }
39638         };
39639         
39640         return cfg;
39641     },
39642     
39643     getChildContainer: function( )
39644     {
39645         if (this.boxesEl) {
39646             return this.boxesEl;
39647         }
39648         
39649         this.boxesEl = this.el.select('.mas-boxes').first();
39650         
39651         return this.boxesEl;
39652     },
39653     
39654     
39655     initEvents : function()
39656     {
39657         var _this = this;
39658         
39659         if(this.isAutoInitial){
39660             Roo.log('hook children rendered');
39661             this.on('childrenrendered', function() {
39662                 Roo.log('children rendered');
39663                 _this.initial();
39664             } ,this);
39665         }
39666         
39667     },
39668     
39669     initial : function()
39670     {
39671         this.reloadItems();
39672
39673         this.currentSize = this.el.getBox(true);
39674
39675         /// was window resize... - let's see if this works..
39676         Roo.EventManager.onWindowResize(this.resize, this); 
39677
39678         if(!this.isAutoInitial){
39679             this.layout();
39680             return;
39681         }
39682         
39683         this.layout.defer(500,this);
39684     },
39685     
39686     reloadItems: function()
39687     {
39688         this.bricks = this.el.select('.masonry-brick', true);
39689         
39690         this.bricks.each(function(b) {
39691             //Roo.log(b.getSize());
39692             if (!b.attr('originalwidth')) {
39693                 b.attr('originalwidth',  b.getSize().width);
39694             }
39695             
39696         });
39697         
39698         Roo.log(this.bricks.elements.length);
39699     },
39700     
39701     resize : function()
39702     {
39703         Roo.log('resize');
39704         var cs = this.el.getBox(true);
39705         
39706         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
39707             Roo.log("no change in with or X");
39708             return;
39709         }
39710         this.currentSize = cs;
39711         this.layout();
39712     },
39713     
39714     layout : function()
39715     {
39716          Roo.log('layout');
39717         this._resetLayout();
39718         //this._manageStamps();
39719       
39720         // don't animate first layout
39721         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39722         this.layoutItems( isInstant );
39723       
39724         // flag for initalized
39725         this._isLayoutInited = true;
39726     },
39727     
39728     layoutItems : function( isInstant )
39729     {
39730         //var items = this._getItemsForLayout( this.items );
39731         // original code supports filtering layout items.. we just ignore it..
39732         
39733         this._layoutItems( this.bricks , isInstant );
39734       
39735         this._postLayout();
39736     },
39737     _layoutItems : function ( items , isInstant)
39738     {
39739        //this.fireEvent( 'layout', this, items );
39740     
39741
39742         if ( !items || !items.elements.length ) {
39743           // no items, emit event with empty array
39744             return;
39745         }
39746
39747         var queue = [];
39748         items.each(function(item) {
39749             Roo.log("layout item");
39750             Roo.log(item);
39751             // get x/y object from method
39752             var position = this._getItemLayoutPosition( item );
39753             // enqueue
39754             position.item = item;
39755             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
39756             queue.push( position );
39757         }, this);
39758       
39759         this._processLayoutQueue( queue );
39760     },
39761     /** Sets position of item in DOM
39762     * @param {Element} item
39763     * @param {Number} x - horizontal position
39764     * @param {Number} y - vertical position
39765     * @param {Boolean} isInstant - disables transitions
39766     */
39767     _processLayoutQueue : function( queue )
39768     {
39769         for ( var i=0, len = queue.length; i < len; i++ ) {
39770             var obj = queue[i];
39771             obj.item.position('absolute');
39772             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
39773         }
39774     },
39775       
39776     
39777     /**
39778     * Any logic you want to do after each layout,
39779     * i.e. size the container
39780     */
39781     _postLayout : function()
39782     {
39783         this.resizeContainer();
39784     },
39785     
39786     resizeContainer : function()
39787     {
39788         if ( !this.isResizingContainer ) {
39789             return;
39790         }
39791         var size = this._getContainerSize();
39792         if ( size ) {
39793             this.el.setSize(size.width,size.height);
39794             this.boxesEl.setSize(size.width,size.height);
39795         }
39796     },
39797     
39798     
39799     
39800     _resetLayout : function()
39801     {
39802         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
39803         this.colWidth = this.el.getWidth();
39804         //this.gutter = this.el.getWidth(); 
39805         
39806         this.measureColumns();
39807
39808         // reset column Y
39809         var i = this.cols;
39810         this.colYs = [];
39811         while (i--) {
39812             this.colYs.push( 0 );
39813         }
39814     
39815         this.maxY = 0;
39816     },
39817
39818     measureColumns : function()
39819     {
39820         this.getContainerWidth();
39821       // if columnWidth is 0, default to outerWidth of first item
39822         if ( !this.columnWidth ) {
39823             var firstItem = this.bricks.first();
39824             Roo.log(firstItem);
39825             this.columnWidth  = this.containerWidth;
39826             if (firstItem && firstItem.attr('originalwidth') ) {
39827                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
39828             }
39829             // columnWidth fall back to item of first element
39830             Roo.log("set column width?");
39831                         this.initialColumnWidth = this.columnWidth  ;
39832
39833             // if first elem has no width, default to size of container
39834             
39835         }
39836         
39837         
39838         if (this.initialColumnWidth) {
39839             this.columnWidth = this.initialColumnWidth;
39840         }
39841         
39842         
39843             
39844         // column width is fixed at the top - however if container width get's smaller we should
39845         // reduce it...
39846         
39847         // this bit calcs how man columns..
39848             
39849         var columnWidth = this.columnWidth += this.gutter;
39850       
39851         // calculate columns
39852         var containerWidth = this.containerWidth + this.gutter;
39853         
39854         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
39855         // fix rounding errors, typically with gutters
39856         var excess = columnWidth - containerWidth % columnWidth;
39857         
39858         
39859         // if overshoot is less than a pixel, round up, otherwise floor it
39860         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
39861         cols = Math[ mathMethod ]( cols );
39862         this.cols = Math.max( cols, 1 );
39863         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39864         
39865          // padding positioning..
39866         var totalColWidth = this.cols * this.columnWidth;
39867         var padavail = this.containerWidth - totalColWidth;
39868         // so for 2 columns - we need 3 'pads'
39869         
39870         var padNeeded = (1+this.cols) * this.padWidth;
39871         
39872         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
39873         
39874         this.columnWidth += padExtra
39875         //this.padWidth = Math.floor(padavail /  ( this.cols));
39876         
39877         // adjust colum width so that padding is fixed??
39878         
39879         // we have 3 columns ... total = width * 3
39880         // we have X left over... that should be used by 
39881         
39882         //if (this.expandC) {
39883             
39884         //}
39885         
39886         
39887         
39888     },
39889     
39890     getContainerWidth : function()
39891     {
39892        /* // container is parent if fit width
39893         var container = this.isFitWidth ? this.element.parentNode : this.element;
39894         // check that this.size and size are there
39895         // IE8 triggers resize on body size change, so they might not be
39896         
39897         var size = getSize( container );  //FIXME
39898         this.containerWidth = size && size.innerWidth; //FIXME
39899         */
39900          
39901         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39902         
39903     },
39904     
39905     _getItemLayoutPosition : function( item )  // what is item?
39906     {
39907         // we resize the item to our columnWidth..
39908       
39909         item.setWidth(this.columnWidth);
39910         item.autoBoxAdjust  = false;
39911         
39912         var sz = item.getSize();
39913  
39914         // how many columns does this brick span
39915         var remainder = this.containerWidth % this.columnWidth;
39916         
39917         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
39918         // round if off by 1 pixel, otherwise use ceil
39919         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
39920         colSpan = Math.min( colSpan, this.cols );
39921         
39922         // normally this should be '1' as we dont' currently allow multi width columns..
39923         
39924         var colGroup = this._getColGroup( colSpan );
39925         // get the minimum Y value from the columns
39926         var minimumY = Math.min.apply( Math, colGroup );
39927         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39928         
39929         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
39930          
39931         // position the brick
39932         var position = {
39933             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
39934             y: this.currentSize.y + minimumY + this.padHeight
39935         };
39936         
39937         Roo.log(position);
39938         // apply setHeight to necessary columns
39939         var setHeight = minimumY + sz.height + this.padHeight;
39940         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39941         
39942         var setSpan = this.cols + 1 - colGroup.length;
39943         for ( var i = 0; i < setSpan; i++ ) {
39944           this.colYs[ shortColIndex + i ] = setHeight ;
39945         }
39946       
39947         return position;
39948     },
39949     
39950     /**
39951      * @param {Number} colSpan - number of columns the element spans
39952      * @returns {Array} colGroup
39953      */
39954     _getColGroup : function( colSpan )
39955     {
39956         if ( colSpan < 2 ) {
39957           // if brick spans only one column, use all the column Ys
39958           return this.colYs;
39959         }
39960       
39961         var colGroup = [];
39962         // how many different places could this brick fit horizontally
39963         var groupCount = this.cols + 1 - colSpan;
39964         // for each group potential horizontal position
39965         for ( var i = 0; i < groupCount; i++ ) {
39966           // make an array of colY values for that one group
39967           var groupColYs = this.colYs.slice( i, i + colSpan );
39968           // and get the max value of the array
39969           colGroup[i] = Math.max.apply( Math, groupColYs );
39970         }
39971         return colGroup;
39972     },
39973     /*
39974     _manageStamp : function( stamp )
39975     {
39976         var stampSize =  stamp.getSize();
39977         var offset = stamp.getBox();
39978         // get the columns that this stamp affects
39979         var firstX = this.isOriginLeft ? offset.x : offset.right;
39980         var lastX = firstX + stampSize.width;
39981         var firstCol = Math.floor( firstX / this.columnWidth );
39982         firstCol = Math.max( 0, firstCol );
39983         
39984         var lastCol = Math.floor( lastX / this.columnWidth );
39985         // lastCol should not go over if multiple of columnWidth #425
39986         lastCol -= lastX % this.columnWidth ? 0 : 1;
39987         lastCol = Math.min( this.cols - 1, lastCol );
39988         
39989         // set colYs to bottom of the stamp
39990         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
39991             stampSize.height;
39992             
39993         for ( var i = firstCol; i <= lastCol; i++ ) {
39994           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
39995         }
39996     },
39997     */
39998     
39999     _getContainerSize : function()
40000     {
40001         this.maxY = Math.max.apply( Math, this.colYs );
40002         var size = {
40003             height: this.maxY
40004         };
40005       
40006         if ( this.isFitWidth ) {
40007             size.width = this._getContainerFitWidth();
40008         }
40009       
40010         return size;
40011     },
40012     
40013     _getContainerFitWidth : function()
40014     {
40015         var unusedCols = 0;
40016         // count unused columns
40017         var i = this.cols;
40018         while ( --i ) {
40019           if ( this.colYs[i] !== 0 ) {
40020             break;
40021           }
40022           unusedCols++;
40023         }
40024         // fit container to columns that have been used
40025         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40026     },
40027     
40028     needsResizeLayout : function()
40029     {
40030         var previousWidth = this.containerWidth;
40031         this.getContainerWidth();
40032         return previousWidth !== this.containerWidth;
40033     }
40034  
40035 });
40036
40037  
40038
40039  /*
40040  * - LGPL
40041  *
40042  * element
40043  * 
40044  */
40045
40046 /**
40047  * @class Roo.bootstrap.MasonryBrick
40048  * @extends Roo.bootstrap.Component
40049  * Bootstrap MasonryBrick class
40050  * 
40051  * @constructor
40052  * Create a new MasonryBrick
40053  * @param {Object} config The config object
40054  */
40055
40056 Roo.bootstrap.MasonryBrick = function(config){
40057     
40058     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40059     
40060     Roo.bootstrap.MasonryBrick.register(this);
40061     
40062     this.addEvents({
40063         // raw events
40064         /**
40065          * @event click
40066          * When a MasonryBrick is clcik
40067          * @param {Roo.bootstrap.MasonryBrick} this
40068          * @param {Roo.EventObject} e
40069          */
40070         "click" : true
40071     });
40072 };
40073
40074 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40075     
40076     /**
40077      * @cfg {String} title
40078      */   
40079     title : '',
40080     /**
40081      * @cfg {String} html
40082      */   
40083     html : '',
40084     /**
40085      * @cfg {String} bgimage
40086      */   
40087     bgimage : '',
40088     /**
40089      * @cfg {String} videourl
40090      */   
40091     videourl : '',
40092     /**
40093      * @cfg {String} cls
40094      */   
40095     cls : '',
40096     /**
40097      * @cfg {String} href
40098      */   
40099     href : '',
40100     /**
40101      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40102      */   
40103     size : 'xs',
40104     
40105     /**
40106      * @cfg {String} placetitle (center|bottom)
40107      */   
40108     placetitle : '',
40109     
40110     /**
40111      * @cfg {Boolean} isFitContainer defalut true
40112      */   
40113     isFitContainer : true, 
40114     
40115     /**
40116      * @cfg {Boolean} preventDefault defalut false
40117      */   
40118     preventDefault : false, 
40119     
40120     /**
40121      * @cfg {Boolean} inverse defalut false
40122      */   
40123     maskInverse : false, 
40124     
40125     getAutoCreate : function()
40126     {
40127         if(!this.isFitContainer){
40128             return this.getSplitAutoCreate();
40129         }
40130         
40131         var cls = 'masonry-brick masonry-brick-full';
40132         
40133         if(this.href.length){
40134             cls += ' masonry-brick-link';
40135         }
40136         
40137         if(this.bgimage.length){
40138             cls += ' masonry-brick-image';
40139         }
40140         
40141         if(this.maskInverse){
40142             cls += ' mask-inverse';
40143         }
40144         
40145         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40146             cls += ' enable-mask';
40147         }
40148         
40149         if(this.size){
40150             cls += ' masonry-' + this.size + '-brick';
40151         }
40152         
40153         if(this.placetitle.length){
40154             
40155             switch (this.placetitle) {
40156                 case 'center' :
40157                     cls += ' masonry-center-title';
40158                     break;
40159                 case 'bottom' :
40160                     cls += ' masonry-bottom-title';
40161                     break;
40162                 default:
40163                     break;
40164             }
40165             
40166         } else {
40167             if(!this.html.length && !this.bgimage.length){
40168                 cls += ' masonry-center-title';
40169             }
40170
40171             if(!this.html.length && this.bgimage.length){
40172                 cls += ' masonry-bottom-title';
40173             }
40174         }
40175         
40176         if(this.cls){
40177             cls += ' ' + this.cls;
40178         }
40179         
40180         var cfg = {
40181             tag: (this.href.length) ? 'a' : 'div',
40182             cls: cls,
40183             cn: [
40184                 {
40185                     tag: 'div',
40186                     cls: 'masonry-brick-mask'
40187                 },
40188                 {
40189                     tag: 'div',
40190                     cls: 'masonry-brick-paragraph',
40191                     cn: []
40192                 }
40193             ]
40194         };
40195         
40196         if(this.href.length){
40197             cfg.href = this.href;
40198         }
40199         
40200         var cn = cfg.cn[1].cn;
40201         
40202         if(this.title.length){
40203             cn.push({
40204                 tag: 'h4',
40205                 cls: 'masonry-brick-title',
40206                 html: this.title
40207             });
40208         }
40209         
40210         if(this.html.length){
40211             cn.push({
40212                 tag: 'p',
40213                 cls: 'masonry-brick-text',
40214                 html: this.html
40215             });
40216         }
40217         
40218         if (!this.title.length && !this.html.length) {
40219             cfg.cn[1].cls += ' hide';
40220         }
40221         
40222         if(this.bgimage.length){
40223             cfg.cn.push({
40224                 tag: 'img',
40225                 cls: 'masonry-brick-image-view',
40226                 src: this.bgimage
40227             });
40228         }
40229         
40230         if(this.videourl.length){
40231             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40232             // youtube support only?
40233             cfg.cn.push({
40234                 tag: 'iframe',
40235                 cls: 'masonry-brick-image-view',
40236                 src: vurl,
40237                 frameborder : 0,
40238                 allowfullscreen : true
40239             });
40240         }
40241         
40242         return cfg;
40243         
40244     },
40245     
40246     getSplitAutoCreate : function()
40247     {
40248         var cls = 'masonry-brick masonry-brick-split';
40249         
40250         if(this.href.length){
40251             cls += ' masonry-brick-link';
40252         }
40253         
40254         if(this.bgimage.length){
40255             cls += ' masonry-brick-image';
40256         }
40257         
40258         if(this.size){
40259             cls += ' masonry-' + this.size + '-brick';
40260         }
40261         
40262         switch (this.placetitle) {
40263             case 'center' :
40264                 cls += ' masonry-center-title';
40265                 break;
40266             case 'bottom' :
40267                 cls += ' masonry-bottom-title';
40268                 break;
40269             default:
40270                 if(!this.bgimage.length){
40271                     cls += ' masonry-center-title';
40272                 }
40273
40274                 if(this.bgimage.length){
40275                     cls += ' masonry-bottom-title';
40276                 }
40277                 break;
40278         }
40279         
40280         if(this.cls){
40281             cls += ' ' + this.cls;
40282         }
40283         
40284         var cfg = {
40285             tag: (this.href.length) ? 'a' : 'div',
40286             cls: cls,
40287             cn: [
40288                 {
40289                     tag: 'div',
40290                     cls: 'masonry-brick-split-head',
40291                     cn: [
40292                         {
40293                             tag: 'div',
40294                             cls: 'masonry-brick-paragraph',
40295                             cn: []
40296                         }
40297                     ]
40298                 },
40299                 {
40300                     tag: 'div',
40301                     cls: 'masonry-brick-split-body',
40302                     cn: []
40303                 }
40304             ]
40305         };
40306         
40307         if(this.href.length){
40308             cfg.href = this.href;
40309         }
40310         
40311         if(this.title.length){
40312             cfg.cn[0].cn[0].cn.push({
40313                 tag: 'h4',
40314                 cls: 'masonry-brick-title',
40315                 html: this.title
40316             });
40317         }
40318         
40319         if(this.html.length){
40320             cfg.cn[1].cn.push({
40321                 tag: 'p',
40322                 cls: 'masonry-brick-text',
40323                 html: this.html
40324             });
40325         }
40326
40327         if(this.bgimage.length){
40328             cfg.cn[0].cn.push({
40329                 tag: 'img',
40330                 cls: 'masonry-brick-image-view',
40331                 src: this.bgimage
40332             });
40333         }
40334         
40335         if(this.videourl.length){
40336             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40337             // youtube support only?
40338             cfg.cn[0].cn.cn.push({
40339                 tag: 'iframe',
40340                 cls: 'masonry-brick-image-view',
40341                 src: vurl,
40342                 frameborder : 0,
40343                 allowfullscreen : true
40344             });
40345         }
40346         
40347         return cfg;
40348     },
40349     
40350     initEvents: function() 
40351     {
40352         switch (this.size) {
40353             case 'xs' :
40354                 this.x = 1;
40355                 this.y = 1;
40356                 break;
40357             case 'sm' :
40358                 this.x = 2;
40359                 this.y = 2;
40360                 break;
40361             case 'md' :
40362             case 'md-left' :
40363             case 'md-right' :
40364                 this.x = 3;
40365                 this.y = 3;
40366                 break;
40367             case 'tall' :
40368                 this.x = 2;
40369                 this.y = 3;
40370                 break;
40371             case 'wide' :
40372                 this.x = 3;
40373                 this.y = 2;
40374                 break;
40375             case 'wide-thin' :
40376                 this.x = 3;
40377                 this.y = 1;
40378                 break;
40379                         
40380             default :
40381                 break;
40382         }
40383         
40384         if(Roo.isTouch){
40385             this.el.on('touchstart', this.onTouchStart, this);
40386             this.el.on('touchmove', this.onTouchMove, this);
40387             this.el.on('touchend', this.onTouchEnd, this);
40388             this.el.on('contextmenu', this.onContextMenu, this);
40389         } else {
40390             this.el.on('mouseenter'  ,this.enter, this);
40391             this.el.on('mouseleave', this.leave, this);
40392             this.el.on('click', this.onClick, this);
40393         }
40394         
40395         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40396             this.parent().bricks.push(this);   
40397         }
40398         
40399     },
40400     
40401     onClick: function(e, el)
40402     {
40403         var time = this.endTimer - this.startTimer;
40404         // Roo.log(e.preventDefault());
40405         if(Roo.isTouch){
40406             if(time > 1000){
40407                 e.preventDefault();
40408                 return;
40409             }
40410         }
40411         
40412         if(!this.preventDefault){
40413             return;
40414         }
40415         
40416         e.preventDefault();
40417         
40418         if (this.activeClass != '') {
40419             this.selectBrick();
40420         }
40421         
40422         this.fireEvent('click', this, e);
40423     },
40424     
40425     enter: function(e, el)
40426     {
40427         e.preventDefault();
40428         
40429         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40430             return;
40431         }
40432         
40433         if(this.bgimage.length && this.html.length){
40434             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40435         }
40436     },
40437     
40438     leave: function(e, el)
40439     {
40440         e.preventDefault();
40441         
40442         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40443             return;
40444         }
40445         
40446         if(this.bgimage.length && this.html.length){
40447             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40448         }
40449     },
40450     
40451     onTouchStart: function(e, el)
40452     {
40453 //        e.preventDefault();
40454         
40455         this.touchmoved = false;
40456         
40457         if(!this.isFitContainer){
40458             return;
40459         }
40460         
40461         if(!this.bgimage.length || !this.html.length){
40462             return;
40463         }
40464         
40465         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40466         
40467         this.timer = new Date().getTime();
40468         
40469     },
40470     
40471     onTouchMove: function(e, el)
40472     {
40473         this.touchmoved = true;
40474     },
40475     
40476     onContextMenu : function(e,el)
40477     {
40478         e.preventDefault();
40479         e.stopPropagation();
40480         return false;
40481     },
40482     
40483     onTouchEnd: function(e, el)
40484     {
40485 //        e.preventDefault();
40486         
40487         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40488         
40489             this.leave(e,el);
40490             
40491             return;
40492         }
40493         
40494         if(!this.bgimage.length || !this.html.length){
40495             
40496             if(this.href.length){
40497                 window.location.href = this.href;
40498             }
40499             
40500             return;
40501         }
40502         
40503         if(!this.isFitContainer){
40504             return;
40505         }
40506         
40507         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40508         
40509         window.location.href = this.href;
40510     },
40511     
40512     //selection on single brick only
40513     selectBrick : function() {
40514         
40515         if (!this.parentId) {
40516             return;
40517         }
40518         
40519         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40520         var index = m.selectedBrick.indexOf(this.id);
40521         
40522         if ( index > -1) {
40523             m.selectedBrick.splice(index,1);
40524             this.el.removeClass(this.activeClass);
40525             return;
40526         }
40527         
40528         for(var i = 0; i < m.selectedBrick.length; i++) {
40529             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40530             b.el.removeClass(b.activeClass);
40531         }
40532         
40533         m.selectedBrick = [];
40534         
40535         m.selectedBrick.push(this.id);
40536         this.el.addClass(this.activeClass);
40537         return;
40538     },
40539     
40540     isSelected : function(){
40541         return this.el.hasClass(this.activeClass);
40542         
40543     }
40544 });
40545
40546 Roo.apply(Roo.bootstrap.MasonryBrick, {
40547     
40548     //groups: {},
40549     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40550      /**
40551     * register a Masonry Brick
40552     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40553     */
40554     
40555     register : function(brick)
40556     {
40557         //this.groups[brick.id] = brick;
40558         this.groups.add(brick.id, brick);
40559     },
40560     /**
40561     * fetch a  masonry brick based on the masonry brick ID
40562     * @param {string} the masonry brick to add
40563     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
40564     */
40565     
40566     get: function(brick_id) 
40567     {
40568         // if (typeof(this.groups[brick_id]) == 'undefined') {
40569         //     return false;
40570         // }
40571         // return this.groups[brick_id] ;
40572         
40573         if(this.groups.key(brick_id)) {
40574             return this.groups.key(brick_id);
40575         }
40576         
40577         return false;
40578     }
40579     
40580     
40581     
40582 });
40583
40584  /*
40585  * - LGPL
40586  *
40587  * element
40588  * 
40589  */
40590
40591 /**
40592  * @class Roo.bootstrap.Brick
40593  * @extends Roo.bootstrap.Component
40594  * Bootstrap Brick class
40595  * 
40596  * @constructor
40597  * Create a new Brick
40598  * @param {Object} config The config object
40599  */
40600
40601 Roo.bootstrap.Brick = function(config){
40602     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
40603     
40604     this.addEvents({
40605         // raw events
40606         /**
40607          * @event click
40608          * When a Brick is click
40609          * @param {Roo.bootstrap.Brick} this
40610          * @param {Roo.EventObject} e
40611          */
40612         "click" : true
40613     });
40614 };
40615
40616 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
40617     
40618     /**
40619      * @cfg {String} title
40620      */   
40621     title : '',
40622     /**
40623      * @cfg {String} html
40624      */   
40625     html : '',
40626     /**
40627      * @cfg {String} bgimage
40628      */   
40629     bgimage : '',
40630     /**
40631      * @cfg {String} cls
40632      */   
40633     cls : '',
40634     /**
40635      * @cfg {String} href
40636      */   
40637     href : '',
40638     /**
40639      * @cfg {String} video
40640      */   
40641     video : '',
40642     /**
40643      * @cfg {Boolean} square
40644      */   
40645     square : true,
40646     
40647     getAutoCreate : function()
40648     {
40649         var cls = 'roo-brick';
40650         
40651         if(this.href.length){
40652             cls += ' roo-brick-link';
40653         }
40654         
40655         if(this.bgimage.length){
40656             cls += ' roo-brick-image';
40657         }
40658         
40659         if(!this.html.length && !this.bgimage.length){
40660             cls += ' roo-brick-center-title';
40661         }
40662         
40663         if(!this.html.length && this.bgimage.length){
40664             cls += ' roo-brick-bottom-title';
40665         }
40666         
40667         if(this.cls){
40668             cls += ' ' + this.cls;
40669         }
40670         
40671         var cfg = {
40672             tag: (this.href.length) ? 'a' : 'div',
40673             cls: cls,
40674             cn: [
40675                 {
40676                     tag: 'div',
40677                     cls: 'roo-brick-paragraph',
40678                     cn: []
40679                 }
40680             ]
40681         };
40682         
40683         if(this.href.length){
40684             cfg.href = this.href;
40685         }
40686         
40687         var cn = cfg.cn[0].cn;
40688         
40689         if(this.title.length){
40690             cn.push({
40691                 tag: 'h4',
40692                 cls: 'roo-brick-title',
40693                 html: this.title
40694             });
40695         }
40696         
40697         if(this.html.length){
40698             cn.push({
40699                 tag: 'p',
40700                 cls: 'roo-brick-text',
40701                 html: this.html
40702             });
40703         } else {
40704             cn.cls += ' hide';
40705         }
40706         
40707         if(this.bgimage.length){
40708             cfg.cn.push({
40709                 tag: 'img',
40710                 cls: 'roo-brick-image-view',
40711                 src: this.bgimage
40712             });
40713         }
40714         
40715         return cfg;
40716     },
40717     
40718     initEvents: function() 
40719     {
40720         if(this.title.length || this.html.length){
40721             this.el.on('mouseenter'  ,this.enter, this);
40722             this.el.on('mouseleave', this.leave, this);
40723         }
40724         
40725         Roo.EventManager.onWindowResize(this.resize, this); 
40726         
40727         if(this.bgimage.length){
40728             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
40729             this.imageEl.on('load', this.onImageLoad, this);
40730             return;
40731         }
40732         
40733         this.resize();
40734     },
40735     
40736     onImageLoad : function()
40737     {
40738         this.resize();
40739     },
40740     
40741     resize : function()
40742     {
40743         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
40744         
40745         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
40746         
40747         if(this.bgimage.length){
40748             var image = this.el.select('.roo-brick-image-view', true).first();
40749             
40750             image.setWidth(paragraph.getWidth());
40751             
40752             if(this.square){
40753                 image.setHeight(paragraph.getWidth());
40754             }
40755             
40756             this.el.setHeight(image.getHeight());
40757             paragraph.setHeight(image.getHeight());
40758             
40759         }
40760         
40761     },
40762     
40763     enter: function(e, el)
40764     {
40765         e.preventDefault();
40766         
40767         if(this.bgimage.length){
40768             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
40769             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
40770         }
40771     },
40772     
40773     leave: function(e, el)
40774     {
40775         e.preventDefault();
40776         
40777         if(this.bgimage.length){
40778             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
40779             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
40780         }
40781     }
40782     
40783 });
40784
40785  
40786
40787  /*
40788  * - LGPL
40789  *
40790  * Number field 
40791  */
40792
40793 /**
40794  * @class Roo.bootstrap.form.NumberField
40795  * @extends Roo.bootstrap.form.Input
40796  * Bootstrap NumberField class
40797  * 
40798  * 
40799  * 
40800  * 
40801  * @constructor
40802  * Create a new NumberField
40803  * @param {Object} config The config object
40804  */
40805
40806 Roo.bootstrap.form.NumberField = function(config){
40807     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
40808 };
40809
40810 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
40811     
40812     /**
40813      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40814      */
40815     allowDecimals : true,
40816     /**
40817      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40818      */
40819     decimalSeparator : ".",
40820     /**
40821      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40822      */
40823     decimalPrecision : 2,
40824     /**
40825      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40826      */
40827     allowNegative : true,
40828     
40829     /**
40830      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40831      */
40832     allowZero: true,
40833     /**
40834      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40835      */
40836     minValue : Number.NEGATIVE_INFINITY,
40837     /**
40838      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40839      */
40840     maxValue : Number.MAX_VALUE,
40841     /**
40842      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40843      */
40844     minText : "The minimum value for this field is {0}",
40845     /**
40846      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40847      */
40848     maxText : "The maximum value for this field is {0}",
40849     /**
40850      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40851      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40852      */
40853     nanText : "{0} is not a valid number",
40854     /**
40855      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40856      */
40857     thousandsDelimiter : false,
40858     /**
40859      * @cfg {String} valueAlign alignment of value
40860      */
40861     valueAlign : "left",
40862
40863     getAutoCreate : function()
40864     {
40865         var hiddenInput = {
40866             tag: 'input',
40867             type: 'hidden',
40868             id: Roo.id(),
40869             cls: 'hidden-number-input'
40870         };
40871         
40872         if (this.name) {
40873             hiddenInput.name = this.name;
40874         }
40875         
40876         this.name = '';
40877         
40878         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
40879         
40880         this.name = hiddenInput.name;
40881         
40882         if(cfg.cn.length > 0) {
40883             cfg.cn.push(hiddenInput);
40884         }
40885         
40886         return cfg;
40887     },
40888
40889     // private
40890     initEvents : function()
40891     {   
40892         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
40893         
40894         var allowed = "0123456789";
40895         
40896         if(this.allowDecimals){
40897             allowed += this.decimalSeparator;
40898         }
40899         
40900         if(this.allowNegative){
40901             allowed += "-";
40902         }
40903         
40904         if(this.thousandsDelimiter) {
40905             allowed += ",";
40906         }
40907         
40908         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40909         
40910         var keyPress = function(e){
40911             
40912             var k = e.getKey();
40913             
40914             var c = e.getCharCode();
40915             
40916             if(
40917                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40918                     allowed.indexOf(String.fromCharCode(c)) === -1
40919             ){
40920                 e.stopEvent();
40921                 return;
40922             }
40923             
40924             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40925                 return;
40926             }
40927             
40928             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40929                 e.stopEvent();
40930             }
40931         };
40932         
40933         this.el.on("keypress", keyPress, this);
40934     },
40935     
40936     validateValue : function(value)
40937     {
40938         
40939         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
40940             return false;
40941         }
40942         
40943         var num = this.parseValue(value);
40944         
40945         if(isNaN(num)){
40946             this.markInvalid(String.format(this.nanText, value));
40947             return false;
40948         }
40949         
40950         if(num < this.minValue){
40951             this.markInvalid(String.format(this.minText, this.minValue));
40952             return false;
40953         }
40954         
40955         if(num > this.maxValue){
40956             this.markInvalid(String.format(this.maxText, this.maxValue));
40957             return false;
40958         }
40959         
40960         return true;
40961     },
40962
40963     getValue : function()
40964     {
40965         var v = this.hiddenEl().getValue();
40966         
40967         return this.fixPrecision(this.parseValue(v));
40968     },
40969
40970     parseValue : function(value)
40971     {
40972         if(this.thousandsDelimiter) {
40973             value += "";
40974             r = new RegExp(",", "g");
40975             value = value.replace(r, "");
40976         }
40977         
40978         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40979         return isNaN(value) ? '' : value;
40980     },
40981
40982     fixPrecision : function(value)
40983     {
40984         if(this.thousandsDelimiter) {
40985             value += "";
40986             r = new RegExp(",", "g");
40987             value = value.replace(r, "");
40988         }
40989         
40990         var nan = isNaN(value);
40991         
40992         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40993             return nan ? '' : value;
40994         }
40995         return parseFloat(value).toFixed(this.decimalPrecision);
40996     },
40997
40998     setValue : function(v)
40999     {
41000         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41001         
41002         this.value = v;
41003         
41004         if(this.rendered){
41005             
41006             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41007             
41008             this.inputEl().dom.value = (v == '') ? '' :
41009                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41010             
41011             if(!this.allowZero && v === '0') {
41012                 this.hiddenEl().dom.value = '';
41013                 this.inputEl().dom.value = '';
41014             }
41015             
41016             this.validate();
41017         }
41018     },
41019
41020     decimalPrecisionFcn : function(v)
41021     {
41022         return Math.floor(v);
41023     },
41024
41025     beforeBlur : function()
41026     {
41027         var v = this.parseValue(this.getRawValue());
41028         
41029         if(v || v === 0 || v === ''){
41030             this.setValue(v);
41031         }
41032     },
41033     
41034     hiddenEl : function()
41035     {
41036         return this.el.select('input.hidden-number-input',true).first();
41037     }
41038     
41039 });
41040
41041  
41042
41043 /*
41044 * Licence: LGPL
41045 */
41046
41047 /**
41048  * @class Roo.bootstrap.DocumentSlider
41049  * @extends Roo.bootstrap.Component
41050  * Bootstrap DocumentSlider class
41051  * 
41052  * @constructor
41053  * Create a new DocumentViewer
41054  * @param {Object} config The config object
41055  */
41056
41057 Roo.bootstrap.DocumentSlider = function(config){
41058     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41059     
41060     this.files = [];
41061     
41062     this.addEvents({
41063         /**
41064          * @event initial
41065          * Fire after initEvent
41066          * @param {Roo.bootstrap.DocumentSlider} this
41067          */
41068         "initial" : true,
41069         /**
41070          * @event update
41071          * Fire after update
41072          * @param {Roo.bootstrap.DocumentSlider} this
41073          */
41074         "update" : true,
41075         /**
41076          * @event click
41077          * Fire after click
41078          * @param {Roo.bootstrap.DocumentSlider} this
41079          */
41080         "click" : true
41081     });
41082 };
41083
41084 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41085     
41086     files : false,
41087     
41088     indicator : 0,
41089     
41090     getAutoCreate : function()
41091     {
41092         var cfg = {
41093             tag : 'div',
41094             cls : 'roo-document-slider',
41095             cn : [
41096                 {
41097                     tag : 'div',
41098                     cls : 'roo-document-slider-header',
41099                     cn : [
41100                         {
41101                             tag : 'div',
41102                             cls : 'roo-document-slider-header-title'
41103                         }
41104                     ]
41105                 },
41106                 {
41107                     tag : 'div',
41108                     cls : 'roo-document-slider-body',
41109                     cn : [
41110                         {
41111                             tag : 'div',
41112                             cls : 'roo-document-slider-prev',
41113                             cn : [
41114                                 {
41115                                     tag : 'i',
41116                                     cls : 'fa fa-chevron-left'
41117                                 }
41118                             ]
41119                         },
41120                         {
41121                             tag : 'div',
41122                             cls : 'roo-document-slider-thumb',
41123                             cn : [
41124                                 {
41125                                     tag : 'img',
41126                                     cls : 'roo-document-slider-image'
41127                                 }
41128                             ]
41129                         },
41130                         {
41131                             tag : 'div',
41132                             cls : 'roo-document-slider-next',
41133                             cn : [
41134                                 {
41135                                     tag : 'i',
41136                                     cls : 'fa fa-chevron-right'
41137                                 }
41138                             ]
41139                         }
41140                     ]
41141                 }
41142             ]
41143         };
41144         
41145         return cfg;
41146     },
41147     
41148     initEvents : function()
41149     {
41150         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41151         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41152         
41153         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41154         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41155         
41156         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41157         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41158         
41159         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41160         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41161         
41162         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41163         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41164         
41165         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41166         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41167         
41168         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41169         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41170         
41171         this.thumbEl.on('click', this.onClick, this);
41172         
41173         this.prevIndicator.on('click', this.prev, this);
41174         
41175         this.nextIndicator.on('click', this.next, this);
41176         
41177     },
41178     
41179     initial : function()
41180     {
41181         if(this.files.length){
41182             this.indicator = 1;
41183             this.update()
41184         }
41185         
41186         this.fireEvent('initial', this);
41187     },
41188     
41189     update : function()
41190     {
41191         this.imageEl.attr('src', this.files[this.indicator - 1]);
41192         
41193         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41194         
41195         this.prevIndicator.show();
41196         
41197         if(this.indicator == 1){
41198             this.prevIndicator.hide();
41199         }
41200         
41201         this.nextIndicator.show();
41202         
41203         if(this.indicator == this.files.length){
41204             this.nextIndicator.hide();
41205         }
41206         
41207         this.thumbEl.scrollTo('top');
41208         
41209         this.fireEvent('update', this);
41210     },
41211     
41212     onClick : function(e)
41213     {
41214         e.preventDefault();
41215         
41216         this.fireEvent('click', this);
41217     },
41218     
41219     prev : function(e)
41220     {
41221         e.preventDefault();
41222         
41223         this.indicator = Math.max(1, this.indicator - 1);
41224         
41225         this.update();
41226     },
41227     
41228     next : function(e)
41229     {
41230         e.preventDefault();
41231         
41232         this.indicator = Math.min(this.files.length, this.indicator + 1);
41233         
41234         this.update();
41235     }
41236 });
41237 /*
41238  * - LGPL
41239  *
41240  * RadioSet
41241  *
41242  *
41243  */
41244
41245 /**
41246  * @class Roo.bootstrap.form.RadioSet
41247  * @extends Roo.bootstrap.form.Input
41248  * @children Roo.bootstrap.form.Radio
41249  * Bootstrap RadioSet class
41250  * @cfg {String} indicatorpos (left|right) default left
41251  * @cfg {Boolean} inline (true|false) inline the element (default true)
41252  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41253  * @constructor
41254  * Create a new RadioSet
41255  * @param {Object} config The config object
41256  */
41257
41258 Roo.bootstrap.form.RadioSet = function(config){
41259     
41260     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41261     
41262     this.radioes = [];
41263     
41264     Roo.bootstrap.form.RadioSet.register(this);
41265     
41266     this.addEvents({
41267         /**
41268         * @event check
41269         * Fires when the element is checked or unchecked.
41270         * @param {Roo.bootstrap.form.RadioSet} this This radio
41271         * @param {Roo.bootstrap.form.Radio} item The checked item
41272         */
41273        check : true,
41274        /**
41275         * @event click
41276         * Fires when the element is click.
41277         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41278         * @param {Roo.bootstrap.form.Radio} item The checked item
41279         * @param {Roo.EventObject} e The event object
41280         */
41281        click : true
41282     });
41283     
41284 };
41285
41286 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41287
41288     radioes : false,
41289     
41290     inline : true,
41291     
41292     weight : '',
41293     
41294     indicatorpos : 'left',
41295     
41296     getAutoCreate : function()
41297     {
41298         var label = {
41299             tag : 'label',
41300             cls : 'roo-radio-set-label',
41301             cn : [
41302                 {
41303                     tag : 'span',
41304                     html : this.fieldLabel
41305                 }
41306             ]
41307         };
41308         if (Roo.bootstrap.version == 3) {
41309             
41310             
41311             if(this.indicatorpos == 'left'){
41312                 label.cn.unshift({
41313                     tag : 'i',
41314                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41315                     tooltip : 'This field is required'
41316                 });
41317             } else {
41318                 label.cn.push({
41319                     tag : 'i',
41320                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41321                     tooltip : 'This field is required'
41322                 });
41323             }
41324         }
41325         var items = {
41326             tag : 'div',
41327             cls : 'roo-radio-set-items'
41328         };
41329         
41330         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41331         
41332         if (align === 'left' && this.fieldLabel.length) {
41333             
41334             items = {
41335                 cls : "roo-radio-set-right", 
41336                 cn: [
41337                     items
41338                 ]
41339             };
41340             
41341             if(this.labelWidth > 12){
41342                 label.style = "width: " + this.labelWidth + 'px';
41343             }
41344             
41345             if(this.labelWidth < 13 && this.labelmd == 0){
41346                 this.labelmd = this.labelWidth;
41347             }
41348             
41349             if(this.labellg > 0){
41350                 label.cls += ' col-lg-' + this.labellg;
41351                 items.cls += ' col-lg-' + (12 - this.labellg);
41352             }
41353             
41354             if(this.labelmd > 0){
41355                 label.cls += ' col-md-' + this.labelmd;
41356                 items.cls += ' col-md-' + (12 - this.labelmd);
41357             }
41358             
41359             if(this.labelsm > 0){
41360                 label.cls += ' col-sm-' + this.labelsm;
41361                 items.cls += ' col-sm-' + (12 - this.labelsm);
41362             }
41363             
41364             if(this.labelxs > 0){
41365                 label.cls += ' col-xs-' + this.labelxs;
41366                 items.cls += ' col-xs-' + (12 - this.labelxs);
41367             }
41368         }
41369         
41370         var cfg = {
41371             tag : 'div',
41372             cls : 'roo-radio-set',
41373             cn : [
41374                 {
41375                     tag : 'input',
41376                     cls : 'roo-radio-set-input',
41377                     type : 'hidden',
41378                     name : this.name,
41379                     value : this.value ? this.value :  ''
41380                 },
41381                 label,
41382                 items
41383             ]
41384         };
41385         
41386         if(this.weight.length){
41387             cfg.cls += ' roo-radio-' + this.weight;
41388         }
41389         
41390         if(this.inline) {
41391             cfg.cls += ' roo-radio-set-inline';
41392         }
41393         
41394         var settings=this;
41395         ['xs','sm','md','lg'].map(function(size){
41396             if (settings[size]) {
41397                 cfg.cls += ' col-' + size + '-' + settings[size];
41398             }
41399         });
41400         
41401         return cfg;
41402         
41403     },
41404
41405     initEvents : function()
41406     {
41407         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41408         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41409         
41410         if(!this.fieldLabel.length){
41411             this.labelEl.hide();
41412         }
41413         
41414         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41415         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41416         
41417         this.indicator = this.indicatorEl();
41418         
41419         if(this.indicator){
41420             this.indicator.addClass('invisible');
41421         }
41422         
41423         this.originalValue = this.getValue();
41424         
41425     },
41426     
41427     inputEl: function ()
41428     {
41429         return this.el.select('.roo-radio-set-input', true).first();
41430     },
41431     
41432     getChildContainer : function()
41433     {
41434         return this.itemsEl;
41435     },
41436     
41437     register : function(item)
41438     {
41439         this.radioes.push(item);
41440         
41441     },
41442     
41443     validate : function()
41444     {   
41445         if(this.getVisibilityEl().hasClass('hidden')){
41446             return true;
41447         }
41448         
41449         var valid = false;
41450         
41451         Roo.each(this.radioes, function(i){
41452             if(!i.checked){
41453                 return;
41454             }
41455             
41456             valid = true;
41457             return false;
41458         });
41459         
41460         if(this.allowBlank) {
41461             return true;
41462         }
41463         
41464         if(this.disabled || valid){
41465             this.markValid();
41466             return true;
41467         }
41468         
41469         this.markInvalid();
41470         return false;
41471         
41472     },
41473     
41474     markValid : function()
41475     {
41476         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41477             this.indicatorEl().removeClass('visible');
41478             this.indicatorEl().addClass('invisible');
41479         }
41480         
41481         
41482         if (Roo.bootstrap.version == 3) {
41483             this.el.removeClass([this.invalidClass, this.validClass]);
41484             this.el.addClass(this.validClass);
41485         } else {
41486             this.el.removeClass(['is-invalid','is-valid']);
41487             this.el.addClass(['is-valid']);
41488         }
41489         this.fireEvent('valid', this);
41490     },
41491     
41492     markInvalid : function(msg)
41493     {
41494         if(this.allowBlank || this.disabled){
41495             return;
41496         }
41497         
41498         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41499             this.indicatorEl().removeClass('invisible');
41500             this.indicatorEl().addClass('visible');
41501         }
41502         if (Roo.bootstrap.version == 3) {
41503             this.el.removeClass([this.invalidClass, this.validClass]);
41504             this.el.addClass(this.invalidClass);
41505         } else {
41506             this.el.removeClass(['is-invalid','is-valid']);
41507             this.el.addClass(['is-invalid']);
41508         }
41509         
41510         this.fireEvent('invalid', this, msg);
41511         
41512     },
41513     
41514     setValue : function(v, suppressEvent)
41515     {   
41516         if(this.value === v){
41517             return;
41518         }
41519         
41520         this.value = v;
41521         
41522         if(this.rendered){
41523             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41524         }
41525         
41526         Roo.each(this.radioes, function(i){
41527             i.checked = false;
41528             i.el.removeClass('checked');
41529         });
41530         
41531         Roo.each(this.radioes, function(i){
41532             
41533             if(i.value === v || i.value.toString() === v.toString()){
41534                 i.checked = true;
41535                 i.el.addClass('checked');
41536                 
41537                 if(suppressEvent !== true){
41538                     this.fireEvent('check', this, i);
41539                 }
41540                 
41541                 return false;
41542             }
41543             
41544         }, this);
41545         
41546         this.validate();
41547     },
41548     
41549     clearInvalid : function(){
41550         
41551         if(!this.el || this.preventMark){
41552             return;
41553         }
41554         
41555         this.el.removeClass([this.invalidClass]);
41556         
41557         this.fireEvent('valid', this);
41558     }
41559     
41560 });
41561
41562 Roo.apply(Roo.bootstrap.form.RadioSet, {
41563     
41564     groups: {},
41565     
41566     register : function(set)
41567     {
41568         this.groups[set.name] = set;
41569     },
41570     
41571     get: function(name) 
41572     {
41573         if (typeof(this.groups[name]) == 'undefined') {
41574             return false;
41575         }
41576         
41577         return this.groups[name] ;
41578     }
41579     
41580 });
41581 /*
41582  * Based on:
41583  * Ext JS Library 1.1.1
41584  * Copyright(c) 2006-2007, Ext JS, LLC.
41585  *
41586  * Originally Released Under LGPL - original licence link has changed is not relivant.
41587  *
41588  * Fork - LGPL
41589  * <script type="text/javascript">
41590  */
41591
41592
41593 /**
41594  * @class Roo.bootstrap.SplitBar
41595  * @extends Roo.util.Observable
41596  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
41597  * <br><br>
41598  * Usage:
41599  * <pre><code>
41600 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
41601                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
41602 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
41603 split.minSize = 100;
41604 split.maxSize = 600;
41605 split.animate = true;
41606 split.on('moved', splitterMoved);
41607 </code></pre>
41608  * @constructor
41609  * Create a new SplitBar
41610  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
41611  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
41612  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41613  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
41614                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
41615                         position of the SplitBar).
41616  */
41617 Roo.bootstrap.SplitBar = function(cfg){
41618     
41619     /** @private */
41620     
41621     //{
41622     //  dragElement : elm
41623     //  resizingElement: el,
41624         // optional..
41625     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
41626     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
41627         // existingProxy ???
41628     //}
41629     
41630     this.el = Roo.get(cfg.dragElement, true);
41631     this.el.dom.unselectable = "on";
41632     /** @private */
41633     this.resizingEl = Roo.get(cfg.resizingElement, true);
41634
41635     /**
41636      * @private
41637      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41638      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
41639      * @type Number
41640      */
41641     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
41642     
41643     /**
41644      * The minimum size of the resizing element. (Defaults to 0)
41645      * @type Number
41646      */
41647     this.minSize = 0;
41648     
41649     /**
41650      * The maximum size of the resizing element. (Defaults to 2000)
41651      * @type Number
41652      */
41653     this.maxSize = 2000;
41654     
41655     /**
41656      * Whether to animate the transition to the new size
41657      * @type Boolean
41658      */
41659     this.animate = false;
41660     
41661     /**
41662      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
41663      * @type Boolean
41664      */
41665     this.useShim = false;
41666     
41667     /** @private */
41668     this.shim = null;
41669     
41670     if(!cfg.existingProxy){
41671         /** @private */
41672         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
41673     }else{
41674         this.proxy = Roo.get(cfg.existingProxy).dom;
41675     }
41676     /** @private */
41677     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
41678     
41679     /** @private */
41680     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
41681     
41682     /** @private */
41683     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
41684     
41685     /** @private */
41686     this.dragSpecs = {};
41687     
41688     /**
41689      * @private The adapter to use to positon and resize elements
41690      */
41691     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41692     this.adapter.init(this);
41693     
41694     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41695         /** @private */
41696         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
41697         this.el.addClass("roo-splitbar-h");
41698     }else{
41699         /** @private */
41700         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
41701         this.el.addClass("roo-splitbar-v");
41702     }
41703     
41704     this.addEvents({
41705         /**
41706          * @event resize
41707          * Fires when the splitter is moved (alias for {@link #event-moved})
41708          * @param {Roo.bootstrap.SplitBar} this
41709          * @param {Number} newSize the new width or height
41710          */
41711         "resize" : true,
41712         /**
41713          * @event moved
41714          * Fires when the splitter is moved
41715          * @param {Roo.bootstrap.SplitBar} this
41716          * @param {Number} newSize the new width or height
41717          */
41718         "moved" : true,
41719         /**
41720          * @event beforeresize
41721          * Fires before the splitter is dragged
41722          * @param {Roo.bootstrap.SplitBar} this
41723          */
41724         "beforeresize" : true,
41725
41726         "beforeapply" : true
41727     });
41728
41729     Roo.util.Observable.call(this);
41730 };
41731
41732 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
41733     onStartProxyDrag : function(x, y){
41734         this.fireEvent("beforeresize", this);
41735         if(!this.overlay){
41736             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
41737             o.unselectable();
41738             o.enableDisplayMode("block");
41739             // all splitbars share the same overlay
41740             Roo.bootstrap.SplitBar.prototype.overlay = o;
41741         }
41742         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
41743         this.overlay.show();
41744         Roo.get(this.proxy).setDisplayed("block");
41745         var size = this.adapter.getElementSize(this);
41746         this.activeMinSize = this.getMinimumSize();;
41747         this.activeMaxSize = this.getMaximumSize();;
41748         var c1 = size - this.activeMinSize;
41749         var c2 = Math.max(this.activeMaxSize - size, 0);
41750         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41751             this.dd.resetConstraints();
41752             this.dd.setXConstraint(
41753                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
41754                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
41755             );
41756             this.dd.setYConstraint(0, 0);
41757         }else{
41758             this.dd.resetConstraints();
41759             this.dd.setXConstraint(0, 0);
41760             this.dd.setYConstraint(
41761                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
41762                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
41763             );
41764          }
41765         this.dragSpecs.startSize = size;
41766         this.dragSpecs.startPoint = [x, y];
41767         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
41768     },
41769     
41770     /** 
41771      * @private Called after the drag operation by the DDProxy
41772      */
41773     onEndProxyDrag : function(e){
41774         Roo.get(this.proxy).setDisplayed(false);
41775         var endPoint = Roo.lib.Event.getXY(e);
41776         if(this.overlay){
41777             this.overlay.hide();
41778         }
41779         var newSize;
41780         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41781             newSize = this.dragSpecs.startSize + 
41782                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
41783                     endPoint[0] - this.dragSpecs.startPoint[0] :
41784                     this.dragSpecs.startPoint[0] - endPoint[0]
41785                 );
41786         }else{
41787             newSize = this.dragSpecs.startSize + 
41788                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
41789                     endPoint[1] - this.dragSpecs.startPoint[1] :
41790                     this.dragSpecs.startPoint[1] - endPoint[1]
41791                 );
41792         }
41793         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
41794         if(newSize != this.dragSpecs.startSize){
41795             if(this.fireEvent('beforeapply', this, newSize) !== false){
41796                 this.adapter.setElementSize(this, newSize);
41797                 this.fireEvent("moved", this, newSize);
41798                 this.fireEvent("resize", this, newSize);
41799             }
41800         }
41801     },
41802     
41803     /**
41804      * Get the adapter this SplitBar uses
41805      * @return The adapter object
41806      */
41807     getAdapter : function(){
41808         return this.adapter;
41809     },
41810     
41811     /**
41812      * Set the adapter this SplitBar uses
41813      * @param {Object} adapter A SplitBar adapter object
41814      */
41815     setAdapter : function(adapter){
41816         this.adapter = adapter;
41817         this.adapter.init(this);
41818     },
41819     
41820     /**
41821      * Gets the minimum size for the resizing element
41822      * @return {Number} The minimum size
41823      */
41824     getMinimumSize : function(){
41825         return this.minSize;
41826     },
41827     
41828     /**
41829      * Sets the minimum size for the resizing element
41830      * @param {Number} minSize The minimum size
41831      */
41832     setMinimumSize : function(minSize){
41833         this.minSize = minSize;
41834     },
41835     
41836     /**
41837      * Gets the maximum size for the resizing element
41838      * @return {Number} The maximum size
41839      */
41840     getMaximumSize : function(){
41841         return this.maxSize;
41842     },
41843     
41844     /**
41845      * Sets the maximum size for the resizing element
41846      * @param {Number} maxSize The maximum size
41847      */
41848     setMaximumSize : function(maxSize){
41849         this.maxSize = maxSize;
41850     },
41851     
41852     /**
41853      * Sets the initialize size for the resizing element
41854      * @param {Number} size The initial size
41855      */
41856     setCurrentSize : function(size){
41857         var oldAnimate = this.animate;
41858         this.animate = false;
41859         this.adapter.setElementSize(this, size);
41860         this.animate = oldAnimate;
41861     },
41862     
41863     /**
41864      * Destroy this splitbar. 
41865      * @param {Boolean} removeEl True to remove the element
41866      */
41867     destroy : function(removeEl){
41868         if(this.shim){
41869             this.shim.remove();
41870         }
41871         this.dd.unreg();
41872         this.proxy.parentNode.removeChild(this.proxy);
41873         if(removeEl){
41874             this.el.remove();
41875         }
41876     }
41877 });
41878
41879 /**
41880  * @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.
41881  */
41882 Roo.bootstrap.SplitBar.createProxy = function(dir){
41883     var proxy = new Roo.Element(document.createElement("div"));
41884     proxy.unselectable();
41885     var cls = 'roo-splitbar-proxy';
41886     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
41887     document.body.appendChild(proxy.dom);
41888     return proxy.dom;
41889 };
41890
41891 /** 
41892  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
41893  * Default Adapter. It assumes the splitter and resizing element are not positioned
41894  * elements and only gets/sets the width of the element. Generally used for table based layouts.
41895  */
41896 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
41897 };
41898
41899 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
41900     // do nothing for now
41901     init : function(s){
41902     
41903     },
41904     /**
41905      * Called before drag operations to get the current size of the resizing element. 
41906      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41907      */
41908      getElementSize : function(s){
41909         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41910             return s.resizingEl.getWidth();
41911         }else{
41912             return s.resizingEl.getHeight();
41913         }
41914     },
41915     
41916     /**
41917      * Called after drag operations to set the size of the resizing element.
41918      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41919      * @param {Number} newSize The new size to set
41920      * @param {Function} onComplete A function to be invoked when resizing is complete
41921      */
41922     setElementSize : function(s, newSize, onComplete){
41923         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41924             if(!s.animate){
41925                 s.resizingEl.setWidth(newSize);
41926                 if(onComplete){
41927                     onComplete(s, newSize);
41928                 }
41929             }else{
41930                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
41931             }
41932         }else{
41933             
41934             if(!s.animate){
41935                 s.resizingEl.setHeight(newSize);
41936                 if(onComplete){
41937                     onComplete(s, newSize);
41938                 }
41939             }else{
41940                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
41941             }
41942         }
41943     }
41944 };
41945
41946 /** 
41947  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
41948  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
41949  * Adapter that  moves the splitter element to align with the resized sizing element. 
41950  * Used with an absolute positioned SplitBar.
41951  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
41952  * document.body, make sure you assign an id to the body element.
41953  */
41954 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
41955     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41956     this.container = Roo.get(container);
41957 };
41958
41959 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
41960     init : function(s){
41961         this.basic.init(s);
41962     },
41963     
41964     getElementSize : function(s){
41965         return this.basic.getElementSize(s);
41966     },
41967     
41968     setElementSize : function(s, newSize, onComplete){
41969         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
41970     },
41971     
41972     moveSplitter : function(s){
41973         var yes = Roo.bootstrap.SplitBar;
41974         switch(s.placement){
41975             case yes.LEFT:
41976                 s.el.setX(s.resizingEl.getRight());
41977                 break;
41978             case yes.RIGHT:
41979                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
41980                 break;
41981             case yes.TOP:
41982                 s.el.setY(s.resizingEl.getBottom());
41983                 break;
41984             case yes.BOTTOM:
41985                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
41986                 break;
41987         }
41988     }
41989 };
41990
41991 /**
41992  * Orientation constant - Create a vertical SplitBar
41993  * @static
41994  * @type Number
41995  */
41996 Roo.bootstrap.SplitBar.VERTICAL = 1;
41997
41998 /**
41999  * Orientation constant - Create a horizontal SplitBar
42000  * @static
42001  * @type Number
42002  */
42003 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42004
42005 /**
42006  * Placement constant - The resizing element is to the left of the splitter element
42007  * @static
42008  * @type Number
42009  */
42010 Roo.bootstrap.SplitBar.LEFT = 1;
42011
42012 /**
42013  * Placement constant - The resizing element is to the right of the splitter element
42014  * @static
42015  * @type Number
42016  */
42017 Roo.bootstrap.SplitBar.RIGHT = 2;
42018
42019 /**
42020  * Placement constant - The resizing element is positioned above the splitter element
42021  * @static
42022  * @type Number
42023  */
42024 Roo.bootstrap.SplitBar.TOP = 3;
42025
42026 /**
42027  * Placement constant - The resizing element is positioned under splitter element
42028  * @static
42029  * @type Number
42030  */
42031 Roo.bootstrap.SplitBar.BOTTOM = 4;
42032 /*
42033  * Based on:
42034  * Ext JS Library 1.1.1
42035  * Copyright(c) 2006-2007, Ext JS, LLC.
42036  *
42037  * Originally Released Under LGPL - original licence link has changed is not relivant.
42038  *
42039  * Fork - LGPL
42040  * <script type="text/javascript">
42041  */
42042
42043 /**
42044  * @class Roo.bootstrap.layout.Manager
42045  * @extends Roo.bootstrap.Component
42046  * @abstract
42047  * Base class for layout managers.
42048  */
42049 Roo.bootstrap.layout.Manager = function(config)
42050 {
42051     this.monitorWindowResize = true; // do this before we apply configuration.
42052     
42053     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42054
42055
42056
42057
42058
42059     /** false to disable window resize monitoring @type Boolean */
42060     
42061     this.regions = {};
42062     this.addEvents({
42063         /**
42064          * @event layout
42065          * Fires when a layout is performed.
42066          * @param {Roo.LayoutManager} this
42067          */
42068         "layout" : true,
42069         /**
42070          * @event regionresized
42071          * Fires when the user resizes a region.
42072          * @param {Roo.LayoutRegion} region The resized region
42073          * @param {Number} newSize The new size (width for east/west, height for north/south)
42074          */
42075         "regionresized" : true,
42076         /**
42077          * @event regioncollapsed
42078          * Fires when a region is collapsed.
42079          * @param {Roo.LayoutRegion} region The collapsed region
42080          */
42081         "regioncollapsed" : true,
42082         /**
42083          * @event regionexpanded
42084          * Fires when a region is expanded.
42085          * @param {Roo.LayoutRegion} region The expanded region
42086          */
42087         "regionexpanded" : true
42088     });
42089     this.updating = false;
42090
42091     if (config.el) {
42092         this.el = Roo.get(config.el);
42093         this.initEvents();
42094     }
42095
42096 };
42097
42098 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42099
42100
42101     regions : null,
42102
42103     monitorWindowResize : true,
42104
42105
42106     updating : false,
42107
42108
42109     onRender : function(ct, position)
42110     {
42111         if(!this.el){
42112             this.el = Roo.get(ct);
42113             this.initEvents();
42114         }
42115         //this.fireEvent('render',this);
42116     },
42117
42118
42119     initEvents: function()
42120     {
42121
42122
42123         // ie scrollbar fix
42124         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42125             document.body.scroll = "no";
42126         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42127             this.el.position('relative');
42128         }
42129         this.id = this.el.id;
42130         this.el.addClass("roo-layout-container");
42131         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42132         if(this.el.dom != document.body ) {
42133             this.el.on('resize', this.layout,this);
42134             this.el.on('show', this.layout,this);
42135         }
42136
42137     },
42138
42139     /**
42140      * Returns true if this layout is currently being updated
42141      * @return {Boolean}
42142      */
42143     isUpdating : function(){
42144         return this.updating;
42145     },
42146
42147     /**
42148      * Suspend the LayoutManager from doing auto-layouts while
42149      * making multiple add or remove calls
42150      */
42151     beginUpdate : function(){
42152         this.updating = true;
42153     },
42154
42155     /**
42156      * Restore auto-layouts and optionally disable the manager from performing a layout
42157      * @param {Boolean} noLayout true to disable a layout update
42158      */
42159     endUpdate : function(noLayout){
42160         this.updating = false;
42161         if(!noLayout){
42162             this.layout();
42163         }
42164     },
42165
42166     layout: function(){
42167         // abstract...
42168     },
42169
42170     onRegionResized : function(region, newSize){
42171         this.fireEvent("regionresized", region, newSize);
42172         this.layout();
42173     },
42174
42175     onRegionCollapsed : function(region){
42176         this.fireEvent("regioncollapsed", region);
42177     },
42178
42179     onRegionExpanded : function(region){
42180         this.fireEvent("regionexpanded", region);
42181     },
42182
42183     /**
42184      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42185      * performs box-model adjustments.
42186      * @return {Object} The size as an object {width: (the width), height: (the height)}
42187      */
42188     getViewSize : function()
42189     {
42190         var size;
42191         if(this.el.dom != document.body){
42192             size = this.el.getSize();
42193         }else{
42194             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42195         }
42196         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42197         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42198         return size;
42199     },
42200
42201     /**
42202      * Returns the Element this layout is bound to.
42203      * @return {Roo.Element}
42204      */
42205     getEl : function(){
42206         return this.el;
42207     },
42208
42209     /**
42210      * Returns the specified region.
42211      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42212      * @return {Roo.LayoutRegion}
42213      */
42214     getRegion : function(target){
42215         return this.regions[target.toLowerCase()];
42216     },
42217
42218     onWindowResize : function(){
42219         if(this.monitorWindowResize){
42220             this.layout();
42221         }
42222     }
42223 });
42224 /*
42225  * Based on:
42226  * Ext JS Library 1.1.1
42227  * Copyright(c) 2006-2007, Ext JS, LLC.
42228  *
42229  * Originally Released Under LGPL - original licence link has changed is not relivant.
42230  *
42231  * Fork - LGPL
42232  * <script type="text/javascript">
42233  */
42234 /**
42235  * @class Roo.bootstrap.layout.Border
42236  * @extends Roo.bootstrap.layout.Manager
42237  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42238  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42239  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42240  * please see: examples/bootstrap/nested.html<br><br>
42241  
42242 <b>The container the layout is rendered into can be either the body element or any other element.
42243 If it is not the body element, the container needs to either be an absolute positioned element,
42244 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42245 the container size if it is not the body element.</b>
42246
42247 * @constructor
42248 * Create a new Border
42249 * @param {Object} config Configuration options
42250  */
42251 Roo.bootstrap.layout.Border = function(config){
42252     config = config || {};
42253     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42254     
42255     
42256     
42257     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42258         if(config[region]){
42259             config[region].region = region;
42260             this.addRegion(config[region]);
42261         }
42262     },this);
42263     
42264 };
42265
42266 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42267
42268 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42269     
42270         /**
42271          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42272          */
42273         /**
42274          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42275          */
42276         /**
42277          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42278          */
42279         /**
42280          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42281          */
42282         /**
42283          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42284          */
42285         
42286         
42287         
42288         
42289     parent : false, // this might point to a 'nest' or a ???
42290     
42291     /**
42292      * Creates and adds a new region if it doesn't already exist.
42293      * @param {String} target The target region key (north, south, east, west or center).
42294      * @param {Object} config The regions config object
42295      * @return {BorderLayoutRegion} The new region
42296      */
42297     addRegion : function(config)
42298     {
42299         if(!this.regions[config.region]){
42300             var r = this.factory(config);
42301             this.bindRegion(r);
42302         }
42303         return this.regions[config.region];
42304     },
42305
42306     // private (kinda)
42307     bindRegion : function(r){
42308         this.regions[r.config.region] = r;
42309         
42310         r.on("visibilitychange",    this.layout, this);
42311         r.on("paneladded",          this.layout, this);
42312         r.on("panelremoved",        this.layout, this);
42313         r.on("invalidated",         this.layout, this);
42314         r.on("resized",             this.onRegionResized, this);
42315         r.on("collapsed",           this.onRegionCollapsed, this);
42316         r.on("expanded",            this.onRegionExpanded, this);
42317     },
42318
42319     /**
42320      * Performs a layout update.
42321      */
42322     layout : function()
42323     {
42324         if(this.updating) {
42325             return;
42326         }
42327         
42328         // render all the rebions if they have not been done alreayd?
42329         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42330             if(this.regions[region] && !this.regions[region].bodyEl){
42331                 this.regions[region].onRender(this.el)
42332             }
42333         },this);
42334         
42335         var size = this.getViewSize();
42336         var w = size.width;
42337         var h = size.height;
42338         var centerW = w;
42339         var centerH = h;
42340         var centerY = 0;
42341         var centerX = 0;
42342         //var x = 0, y = 0;
42343
42344         var rs = this.regions;
42345         var north = rs["north"];
42346         var south = rs["south"]; 
42347         var west = rs["west"];
42348         var east = rs["east"];
42349         var center = rs["center"];
42350         //if(this.hideOnLayout){ // not supported anymore
42351             //c.el.setStyle("display", "none");
42352         //}
42353         if(north && north.isVisible()){
42354             var b = north.getBox();
42355             var m = north.getMargins();
42356             b.width = w - (m.left+m.right);
42357             b.x = m.left;
42358             b.y = m.top;
42359             centerY = b.height + b.y + m.bottom;
42360             centerH -= centerY;
42361             north.updateBox(this.safeBox(b));
42362         }
42363         if(south && south.isVisible()){
42364             var b = south.getBox();
42365             var m = south.getMargins();
42366             b.width = w - (m.left+m.right);
42367             b.x = m.left;
42368             var totalHeight = (b.height + m.top + m.bottom);
42369             b.y = h - totalHeight + m.top;
42370             centerH -= totalHeight;
42371             south.updateBox(this.safeBox(b));
42372         }
42373         if(west && west.isVisible()){
42374             var b = west.getBox();
42375             var m = west.getMargins();
42376             b.height = centerH - (m.top+m.bottom);
42377             b.x = m.left;
42378             b.y = centerY + m.top;
42379             var totalWidth = (b.width + m.left + m.right);
42380             centerX += totalWidth;
42381             centerW -= totalWidth;
42382             west.updateBox(this.safeBox(b));
42383         }
42384         if(east && east.isVisible()){
42385             var b = east.getBox();
42386             var m = east.getMargins();
42387             b.height = centerH - (m.top+m.bottom);
42388             var totalWidth = (b.width + m.left + m.right);
42389             b.x = w - totalWidth + m.left;
42390             b.y = centerY + m.top;
42391             centerW -= totalWidth;
42392             east.updateBox(this.safeBox(b));
42393         }
42394         if(center){
42395             var m = center.getMargins();
42396             var centerBox = {
42397                 x: centerX + m.left,
42398                 y: centerY + m.top,
42399                 width: centerW - (m.left+m.right),
42400                 height: centerH - (m.top+m.bottom)
42401             };
42402             //if(this.hideOnLayout){
42403                 //center.el.setStyle("display", "block");
42404             //}
42405             center.updateBox(this.safeBox(centerBox));
42406         }
42407         this.el.repaint();
42408         this.fireEvent("layout", this);
42409     },
42410
42411     // private
42412     safeBox : function(box){
42413         box.width = Math.max(0, box.width);
42414         box.height = Math.max(0, box.height);
42415         return box;
42416     },
42417
42418     /**
42419      * Adds a ContentPanel (or subclass) to this layout.
42420      * @param {String} target The target region key (north, south, east, west or center).
42421      * @param {Roo.ContentPanel} panel The panel to add
42422      * @return {Roo.ContentPanel} The added panel
42423      */
42424     add : function(target, panel){
42425          
42426         target = target.toLowerCase();
42427         return this.regions[target].add(panel);
42428     },
42429
42430     /**
42431      * Remove a ContentPanel (or subclass) to this layout.
42432      * @param {String} target The target region key (north, south, east, west or center).
42433      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42434      * @return {Roo.ContentPanel} The removed panel
42435      */
42436     remove : function(target, panel){
42437         target = target.toLowerCase();
42438         return this.regions[target].remove(panel);
42439     },
42440
42441     /**
42442      * Searches all regions for a panel with the specified id
42443      * @param {String} panelId
42444      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42445      */
42446     findPanel : function(panelId){
42447         var rs = this.regions;
42448         for(var target in rs){
42449             if(typeof rs[target] != "function"){
42450                 var p = rs[target].getPanel(panelId);
42451                 if(p){
42452                     return p;
42453                 }
42454             }
42455         }
42456         return null;
42457     },
42458
42459     /**
42460      * Searches all regions for a panel with the specified id and activates (shows) it.
42461      * @param {String/ContentPanel} panelId The panels id or the panel itself
42462      * @return {Roo.ContentPanel} The shown panel or null
42463      */
42464     showPanel : function(panelId) {
42465       var rs = this.regions;
42466       for(var target in rs){
42467          var r = rs[target];
42468          if(typeof r != "function"){
42469             if(r.hasPanel(panelId)){
42470                return r.showPanel(panelId);
42471             }
42472          }
42473       }
42474       return null;
42475    },
42476
42477    /**
42478      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42479      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42480      */
42481    /*
42482     restoreState : function(provider){
42483         if(!provider){
42484             provider = Roo.state.Manager;
42485         }
42486         var sm = new Roo.LayoutStateManager();
42487         sm.init(this, provider);
42488     },
42489 */
42490  
42491  
42492     /**
42493      * Adds a xtype elements to the layout.
42494      * <pre><code>
42495
42496 layout.addxtype({
42497        xtype : 'ContentPanel',
42498        region: 'west',
42499        items: [ .... ]
42500    }
42501 );
42502
42503 layout.addxtype({
42504         xtype : 'NestedLayoutPanel',
42505         region: 'west',
42506         layout: {
42507            center: { },
42508            west: { }   
42509         },
42510         items : [ ... list of content panels or nested layout panels.. ]
42511    }
42512 );
42513 </code></pre>
42514      * @param {Object} cfg Xtype definition of item to add.
42515      */
42516     addxtype : function(cfg)
42517     {
42518         // basically accepts a pannel...
42519         // can accept a layout region..!?!?
42520         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42521         
42522         
42523         // theory?  children can only be panels??
42524         
42525         //if (!cfg.xtype.match(/Panel$/)) {
42526         //    return false;
42527         //}
42528         var ret = false;
42529         
42530         if (typeof(cfg.region) == 'undefined') {
42531             Roo.log("Failed to add Panel, region was not set");
42532             Roo.log(cfg);
42533             return false;
42534         }
42535         var region = cfg.region;
42536         delete cfg.region;
42537         
42538           
42539         var xitems = [];
42540         if (cfg.items) {
42541             xitems = cfg.items;
42542             delete cfg.items;
42543         }
42544         var nb = false;
42545         
42546         if ( region == 'center') {
42547             Roo.log("Center: " + cfg.title);
42548         }
42549         
42550         
42551         switch(cfg.xtype) 
42552         {
42553             case 'Content':  // ContentPanel (el, cfg)
42554             case 'Scroll':  // ContentPanel (el, cfg)
42555             case 'View': 
42556                 cfg.autoCreate = cfg.autoCreate || true;
42557                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42558                 //} else {
42559                 //    var el = this.el.createChild();
42560                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42561                 //}
42562                 
42563                 this.add(region, ret);
42564                 break;
42565             
42566             /*
42567             case 'TreePanel': // our new panel!
42568                 cfg.el = this.el.createChild();
42569                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42570                 this.add(region, ret);
42571                 break;
42572             */
42573             
42574             case 'Nest': 
42575                 // create a new Layout (which is  a Border Layout...
42576                 
42577                 var clayout = cfg.layout;
42578                 clayout.el  = this.el.createChild();
42579                 clayout.items   = clayout.items  || [];
42580                 
42581                 delete cfg.layout;
42582                 
42583                 // replace this exitems with the clayout ones..
42584                 xitems = clayout.items;
42585                  
42586                 // force background off if it's in center...
42587                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42588                     cfg.background = false;
42589                 }
42590                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
42591                 
42592                 
42593                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42594                 //console.log('adding nested layout panel '  + cfg.toSource());
42595                 this.add(region, ret);
42596                 nb = {}; /// find first...
42597                 break;
42598             
42599             case 'Grid':
42600                 
42601                 // needs grid and region
42602                 
42603                 //var el = this.getRegion(region).el.createChild();
42604                 /*
42605                  *var el = this.el.createChild();
42606                 // create the grid first...
42607                 cfg.grid.container = el;
42608                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
42609                 */
42610                 
42611                 if (region == 'center' && this.active ) {
42612                     cfg.background = false;
42613                 }
42614                 
42615                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42616                 
42617                 this.add(region, ret);
42618                 /*
42619                 if (cfg.background) {
42620                     // render grid on panel activation (if panel background)
42621                     ret.on('activate', function(gp) {
42622                         if (!gp.grid.rendered) {
42623                     //        gp.grid.render(el);
42624                         }
42625                     });
42626                 } else {
42627                   //  cfg.grid.render(el);
42628                 }
42629                 */
42630                 break;
42631            
42632            
42633             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
42634                 // it was the old xcomponent building that caused this before.
42635                 // espeically if border is the top element in the tree.
42636                 ret = this;
42637                 break; 
42638                 
42639                     
42640                 
42641                 
42642                 
42643             default:
42644                 /*
42645                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
42646                     
42647                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42648                     this.add(region, ret);
42649                 } else {
42650                 */
42651                     Roo.log(cfg);
42652                     throw "Can not add '" + cfg.xtype + "' to Border";
42653                     return null;
42654              
42655                                 
42656              
42657         }
42658         this.beginUpdate();
42659         // add children..
42660         var region = '';
42661         var abn = {};
42662         Roo.each(xitems, function(i)  {
42663             region = nb && i.region ? i.region : false;
42664             
42665             var add = ret.addxtype(i);
42666            
42667             if (region) {
42668                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
42669                 if (!i.background) {
42670                     abn[region] = nb[region] ;
42671                 }
42672             }
42673             
42674         });
42675         this.endUpdate();
42676
42677         // make the last non-background panel active..
42678         //if (nb) { Roo.log(abn); }
42679         if (nb) {
42680             
42681             for(var r in abn) {
42682                 region = this.getRegion(r);
42683                 if (region) {
42684                     // tried using nb[r], but it does not work..
42685                      
42686                     region.showPanel(abn[r]);
42687                    
42688                 }
42689             }
42690         }
42691         return ret;
42692         
42693     },
42694     
42695     
42696 // private
42697     factory : function(cfg)
42698     {
42699         
42700         var validRegions = Roo.bootstrap.layout.Border.regions;
42701
42702         var target = cfg.region;
42703         cfg.mgr = this;
42704         
42705         var r = Roo.bootstrap.layout;
42706         Roo.log(target);
42707         switch(target){
42708             case "north":
42709                 return new r.North(cfg);
42710             case "south":
42711                 return new r.South(cfg);
42712             case "east":
42713                 return new r.East(cfg);
42714             case "west":
42715                 return new r.West(cfg);
42716             case "center":
42717                 return new r.Center(cfg);
42718         }
42719         throw 'Layout region "'+target+'" not supported.';
42720     }
42721     
42722     
42723 });
42724  /*
42725  * Based on:
42726  * Ext JS Library 1.1.1
42727  * Copyright(c) 2006-2007, Ext JS, LLC.
42728  *
42729  * Originally Released Under LGPL - original licence link has changed is not relivant.
42730  *
42731  * Fork - LGPL
42732  * <script type="text/javascript">
42733  */
42734  
42735 /**
42736  * @class Roo.bootstrap.layout.Basic
42737  * @extends Roo.util.Observable
42738  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42739  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42740  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42741  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
42742  * @cfg {string}   region  the region that it inhabits..
42743  * @cfg {bool}   skipConfig skip config?
42744  * 
42745
42746  */
42747 Roo.bootstrap.layout.Basic = function(config){
42748     
42749     this.mgr = config.mgr;
42750     
42751     this.position = config.region;
42752     
42753     var skipConfig = config.skipConfig;
42754     
42755     this.events = {
42756         /**
42757          * @scope Roo.BasicLayoutRegion
42758          */
42759         
42760         /**
42761          * @event beforeremove
42762          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42763          * @param {Roo.LayoutRegion} this
42764          * @param {Roo.ContentPanel} panel The panel
42765          * @param {Object} e The cancel event object
42766          */
42767         "beforeremove" : true,
42768         /**
42769          * @event invalidated
42770          * Fires when the layout for this region is changed.
42771          * @param {Roo.LayoutRegion} this
42772          */
42773         "invalidated" : true,
42774         /**
42775          * @event visibilitychange
42776          * Fires when this region is shown or hidden 
42777          * @param {Roo.LayoutRegion} this
42778          * @param {Boolean} visibility true or false
42779          */
42780         "visibilitychange" : true,
42781         /**
42782          * @event paneladded
42783          * Fires when a panel is added. 
42784          * @param {Roo.LayoutRegion} this
42785          * @param {Roo.ContentPanel} panel The panel
42786          */
42787         "paneladded" : true,
42788         /**
42789          * @event panelremoved
42790          * Fires when a panel is removed. 
42791          * @param {Roo.LayoutRegion} this
42792          * @param {Roo.ContentPanel} panel The panel
42793          */
42794         "panelremoved" : true,
42795         /**
42796          * @event beforecollapse
42797          * Fires when this region before collapse.
42798          * @param {Roo.LayoutRegion} this
42799          */
42800         "beforecollapse" : true,
42801         /**
42802          * @event collapsed
42803          * Fires when this region is collapsed.
42804          * @param {Roo.LayoutRegion} this
42805          */
42806         "collapsed" : true,
42807         /**
42808          * @event expanded
42809          * Fires when this region is expanded.
42810          * @param {Roo.LayoutRegion} this
42811          */
42812         "expanded" : true,
42813         /**
42814          * @event slideshow
42815          * Fires when this region is slid into view.
42816          * @param {Roo.LayoutRegion} this
42817          */
42818         "slideshow" : true,
42819         /**
42820          * @event slidehide
42821          * Fires when this region slides out of view. 
42822          * @param {Roo.LayoutRegion} this
42823          */
42824         "slidehide" : true,
42825         /**
42826          * @event panelactivated
42827          * Fires when a panel is activated. 
42828          * @param {Roo.LayoutRegion} this
42829          * @param {Roo.ContentPanel} panel The activated panel
42830          */
42831         "panelactivated" : true,
42832         /**
42833          * @event resized
42834          * Fires when the user resizes this region. 
42835          * @param {Roo.LayoutRegion} this
42836          * @param {Number} newSize The new size (width for east/west, height for north/south)
42837          */
42838         "resized" : true
42839     };
42840     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42841     this.panels = new Roo.util.MixedCollection();
42842     this.panels.getKey = this.getPanelId.createDelegate(this);
42843     this.box = null;
42844     this.activePanel = null;
42845     // ensure listeners are added...
42846     
42847     if (config.listeners || config.events) {
42848         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
42849             listeners : config.listeners || {},
42850             events : config.events || {}
42851         });
42852     }
42853     
42854     if(skipConfig !== true){
42855         this.applyConfig(config);
42856     }
42857 };
42858
42859 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
42860 {
42861     getPanelId : function(p){
42862         return p.getId();
42863     },
42864     
42865     applyConfig : function(config){
42866         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42867         this.config = config;
42868         
42869     },
42870     
42871     /**
42872      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42873      * the width, for horizontal (north, south) the height.
42874      * @param {Number} newSize The new width or height
42875      */
42876     resizeTo : function(newSize){
42877         var el = this.el ? this.el :
42878                  (this.activePanel ? this.activePanel.getEl() : null);
42879         if(el){
42880             switch(this.position){
42881                 case "east":
42882                 case "west":
42883                     el.setWidth(newSize);
42884                     this.fireEvent("resized", this, newSize);
42885                 break;
42886                 case "north":
42887                 case "south":
42888                     el.setHeight(newSize);
42889                     this.fireEvent("resized", this, newSize);
42890                 break;                
42891             }
42892         }
42893     },
42894     
42895     getBox : function(){
42896         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42897     },
42898     
42899     getMargins : function(){
42900         return this.margins;
42901     },
42902     
42903     updateBox : function(box){
42904         this.box = box;
42905         var el = this.activePanel.getEl();
42906         el.dom.style.left = box.x + "px";
42907         el.dom.style.top = box.y + "px";
42908         this.activePanel.setSize(box.width, box.height);
42909     },
42910     
42911     /**
42912      * Returns the container element for this region.
42913      * @return {Roo.Element}
42914      */
42915     getEl : function(){
42916         return this.activePanel;
42917     },
42918     
42919     /**
42920      * Returns true if this region is currently visible.
42921      * @return {Boolean}
42922      */
42923     isVisible : function(){
42924         return this.activePanel ? true : false;
42925     },
42926     
42927     setActivePanel : function(panel){
42928         panel = this.getPanel(panel);
42929         if(this.activePanel && this.activePanel != panel){
42930             this.activePanel.setActiveState(false);
42931             this.activePanel.getEl().setLeftTop(-10000,-10000);
42932         }
42933         this.activePanel = panel;
42934         panel.setActiveState(true);
42935         if(this.box){
42936             panel.setSize(this.box.width, this.box.height);
42937         }
42938         this.fireEvent("panelactivated", this, panel);
42939         this.fireEvent("invalidated");
42940     },
42941     
42942     /**
42943      * Show the specified panel.
42944      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42945      * @return {Roo.ContentPanel} The shown panel or null
42946      */
42947     showPanel : function(panel){
42948         panel = this.getPanel(panel);
42949         if(panel){
42950             this.setActivePanel(panel);
42951         }
42952         return panel;
42953     },
42954     
42955     /**
42956      * Get the active panel for this region.
42957      * @return {Roo.ContentPanel} The active panel or null
42958      */
42959     getActivePanel : function(){
42960         return this.activePanel;
42961     },
42962     
42963     /**
42964      * Add the passed ContentPanel(s)
42965      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42966      * @return {Roo.ContentPanel} The panel added (if only one was added)
42967      */
42968     add : function(panel){
42969         if(arguments.length > 1){
42970             for(var i = 0, len = arguments.length; i < len; i++) {
42971                 this.add(arguments[i]);
42972             }
42973             return null;
42974         }
42975         if(this.hasPanel(panel)){
42976             this.showPanel(panel);
42977             return panel;
42978         }
42979         var el = panel.getEl();
42980         if(el.dom.parentNode != this.mgr.el.dom){
42981             this.mgr.el.dom.appendChild(el.dom);
42982         }
42983         if(panel.setRegion){
42984             panel.setRegion(this);
42985         }
42986         this.panels.add(panel);
42987         el.setStyle("position", "absolute");
42988         if(!panel.background){
42989             this.setActivePanel(panel);
42990             if(this.config.initialSize && this.panels.getCount()==1){
42991                 this.resizeTo(this.config.initialSize);
42992             }
42993         }
42994         this.fireEvent("paneladded", this, panel);
42995         return panel;
42996     },
42997     
42998     /**
42999      * Returns true if the panel is in this region.
43000      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43001      * @return {Boolean}
43002      */
43003     hasPanel : function(panel){
43004         if(typeof panel == "object"){ // must be panel obj
43005             panel = panel.getId();
43006         }
43007         return this.getPanel(panel) ? true : false;
43008     },
43009     
43010     /**
43011      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43012      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43013      * @param {Boolean} preservePanel Overrides the config preservePanel option
43014      * @return {Roo.ContentPanel} The panel that was removed
43015      */
43016     remove : function(panel, preservePanel){
43017         panel = this.getPanel(panel);
43018         if(!panel){
43019             return null;
43020         }
43021         var e = {};
43022         this.fireEvent("beforeremove", this, panel, e);
43023         if(e.cancel === true){
43024             return null;
43025         }
43026         var panelId = panel.getId();
43027         this.panels.removeKey(panelId);
43028         return panel;
43029     },
43030     
43031     /**
43032      * Returns the panel specified or null if it's not in this region.
43033      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43034      * @return {Roo.ContentPanel}
43035      */
43036     getPanel : function(id){
43037         if(typeof id == "object"){ // must be panel obj
43038             return id;
43039         }
43040         return this.panels.get(id);
43041     },
43042     
43043     /**
43044      * Returns this regions position (north/south/east/west/center).
43045      * @return {String} 
43046      */
43047     getPosition: function(){
43048         return this.position;    
43049     }
43050 });/*
43051  * Based on:
43052  * Ext JS Library 1.1.1
43053  * Copyright(c) 2006-2007, Ext JS, LLC.
43054  *
43055  * Originally Released Under LGPL - original licence link has changed is not relivant.
43056  *
43057  * Fork - LGPL
43058  * <script type="text/javascript">
43059  */
43060  
43061 /**
43062  * @class Roo.bootstrap.layout.Region
43063  * @extends Roo.bootstrap.layout.Basic
43064  * This class represents a region in a layout manager.
43065  
43066  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43067  * @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})
43068  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43069  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43070  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43071  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43072  * @cfg {String}    title           The title for the region (overrides panel titles)
43073  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43074  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43075  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43076  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43077  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43078  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43079  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43080  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43081  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43082  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43083
43084  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43085  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43086  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43087  * @cfg {Number}    width           For East/West panels
43088  * @cfg {Number}    height          For North/South panels
43089  * @cfg {Boolean}   split           To show the splitter
43090  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43091  * 
43092  * @cfg {string}   cls             Extra CSS classes to add to region
43093  * 
43094  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43095  * @cfg {string}   region  the region that it inhabits..
43096  *
43097
43098  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43099  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43100
43101  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43102  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43103  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43104  */
43105 Roo.bootstrap.layout.Region = function(config)
43106 {
43107     this.applyConfig(config);
43108
43109     var mgr = config.mgr;
43110     var pos = config.region;
43111     config.skipConfig = true;
43112     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43113     
43114     if (mgr.el) {
43115         this.onRender(mgr.el);   
43116     }
43117      
43118     this.visible = true;
43119     this.collapsed = false;
43120     this.unrendered_panels = [];
43121 };
43122
43123 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43124
43125     position: '', // set by wrapper (eg. north/south etc..)
43126     unrendered_panels : null,  // unrendered panels.
43127     
43128     tabPosition : false,
43129     
43130     mgr: false, // points to 'Border'
43131     
43132     
43133     createBody : function(){
43134         /** This region's body element 
43135         * @type Roo.Element */
43136         this.bodyEl = this.el.createChild({
43137                 tag: "div",
43138                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43139         });
43140     },
43141
43142     onRender: function(ctr, pos)
43143     {
43144         var dh = Roo.DomHelper;
43145         /** This region's container element 
43146         * @type Roo.Element */
43147         this.el = dh.append(ctr.dom, {
43148                 tag: "div",
43149                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43150             }, true);
43151         /** This region's title element 
43152         * @type Roo.Element */
43153     
43154         this.titleEl = dh.append(this.el.dom,  {
43155                 tag: "div",
43156                 unselectable: "on",
43157                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43158                 children:[
43159                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43160                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43161                 ]
43162             }, true);
43163         
43164         this.titleEl.enableDisplayMode();
43165         /** This region's title text element 
43166         * @type HTMLElement */
43167         this.titleTextEl = this.titleEl.dom.firstChild;
43168         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43169         /*
43170         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43171         this.closeBtn.enableDisplayMode();
43172         this.closeBtn.on("click", this.closeClicked, this);
43173         this.closeBtn.hide();
43174     */
43175         this.createBody(this.config);
43176         if(this.config.hideWhenEmpty){
43177             this.hide();
43178             this.on("paneladded", this.validateVisibility, this);
43179             this.on("panelremoved", this.validateVisibility, this);
43180         }
43181         if(this.autoScroll){
43182             this.bodyEl.setStyle("overflow", "auto");
43183         }else{
43184             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43185         }
43186         //if(c.titlebar !== false){
43187             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43188                 this.titleEl.hide();
43189             }else{
43190                 this.titleEl.show();
43191                 if(this.config.title){
43192                     this.titleTextEl.innerHTML = this.config.title;
43193                 }
43194             }
43195         //}
43196         if(this.config.collapsed){
43197             this.collapse(true);
43198         }
43199         if(this.config.hidden){
43200             this.hide();
43201         }
43202         
43203         if (this.unrendered_panels && this.unrendered_panels.length) {
43204             for (var i =0;i< this.unrendered_panels.length; i++) {
43205                 this.add(this.unrendered_panels[i]);
43206             }
43207             this.unrendered_panels = null;
43208             
43209         }
43210         
43211     },
43212     
43213     applyConfig : function(c)
43214     {
43215         /*
43216          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43217             var dh = Roo.DomHelper;
43218             if(c.titlebar !== false){
43219                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43220                 this.collapseBtn.on("click", this.collapse, this);
43221                 this.collapseBtn.enableDisplayMode();
43222                 /*
43223                 if(c.showPin === true || this.showPin){
43224                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43225                     this.stickBtn.enableDisplayMode();
43226                     this.stickBtn.on("click", this.expand, this);
43227                     this.stickBtn.hide();
43228                 }
43229                 
43230             }
43231             */
43232             /** This region's collapsed element
43233             * @type Roo.Element */
43234             /*
43235              *
43236             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43237                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43238             ]}, true);
43239             
43240             if(c.floatable !== false){
43241                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43242                this.collapsedEl.on("click", this.collapseClick, this);
43243             }
43244
43245             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43246                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43247                    id: "message", unselectable: "on", style:{"float":"left"}});
43248                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43249              }
43250             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43251             this.expandBtn.on("click", this.expand, this);
43252             
43253         }
43254         
43255         if(this.collapseBtn){
43256             this.collapseBtn.setVisible(c.collapsible == true);
43257         }
43258         
43259         this.cmargins = c.cmargins || this.cmargins ||
43260                          (this.position == "west" || this.position == "east" ?
43261                              {top: 0, left: 2, right:2, bottom: 0} :
43262                              {top: 2, left: 0, right:0, bottom: 2});
43263         */
43264         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43265         
43266         
43267         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43268         
43269         this.autoScroll = c.autoScroll || false;
43270         
43271         
43272        
43273         
43274         this.duration = c.duration || .30;
43275         this.slideDuration = c.slideDuration || .45;
43276         this.config = c;
43277        
43278     },
43279     /**
43280      * Returns true if this region is currently visible.
43281      * @return {Boolean}
43282      */
43283     isVisible : function(){
43284         return this.visible;
43285     },
43286
43287     /**
43288      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43289      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43290      */
43291     //setCollapsedTitle : function(title){
43292     //    title = title || "&#160;";
43293      //   if(this.collapsedTitleTextEl){
43294       //      this.collapsedTitleTextEl.innerHTML = title;
43295        // }
43296     //},
43297
43298     getBox : function(){
43299         var b;
43300       //  if(!this.collapsed){
43301             b = this.el.getBox(false, true);
43302        // }else{
43303           //  b = this.collapsedEl.getBox(false, true);
43304         //}
43305         return b;
43306     },
43307
43308     getMargins : function(){
43309         return this.margins;
43310         //return this.collapsed ? this.cmargins : this.margins;
43311     },
43312 /*
43313     highlight : function(){
43314         this.el.addClass("x-layout-panel-dragover");
43315     },
43316
43317     unhighlight : function(){
43318         this.el.removeClass("x-layout-panel-dragover");
43319     },
43320 */
43321     updateBox : function(box)
43322     {
43323         if (!this.bodyEl) {
43324             return; // not rendered yet..
43325         }
43326         
43327         this.box = box;
43328         if(!this.collapsed){
43329             this.el.dom.style.left = box.x + "px";
43330             this.el.dom.style.top = box.y + "px";
43331             this.updateBody(box.width, box.height);
43332         }else{
43333             this.collapsedEl.dom.style.left = box.x + "px";
43334             this.collapsedEl.dom.style.top = box.y + "px";
43335             this.collapsedEl.setSize(box.width, box.height);
43336         }
43337         if(this.tabs){
43338             this.tabs.autoSizeTabs();
43339         }
43340     },
43341
43342     updateBody : function(w, h)
43343     {
43344         if(w !== null){
43345             this.el.setWidth(w);
43346             w -= this.el.getBorderWidth("rl");
43347             if(this.config.adjustments){
43348                 w += this.config.adjustments[0];
43349             }
43350         }
43351         if(h !== null && h > 0){
43352             this.el.setHeight(h);
43353             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43354             h -= this.el.getBorderWidth("tb");
43355             if(this.config.adjustments){
43356                 h += this.config.adjustments[1];
43357             }
43358             this.bodyEl.setHeight(h);
43359             if(this.tabs){
43360                 h = this.tabs.syncHeight(h);
43361             }
43362         }
43363         if(this.panelSize){
43364             w = w !== null ? w : this.panelSize.width;
43365             h = h !== null ? h : this.panelSize.height;
43366         }
43367         if(this.activePanel){
43368             var el = this.activePanel.getEl();
43369             w = w !== null ? w : el.getWidth();
43370             h = h !== null ? h : el.getHeight();
43371             this.panelSize = {width: w, height: h};
43372             this.activePanel.setSize(w, h);
43373         }
43374         if(Roo.isIE && this.tabs){
43375             this.tabs.el.repaint();
43376         }
43377     },
43378
43379     /**
43380      * Returns the container element for this region.
43381      * @return {Roo.Element}
43382      */
43383     getEl : function(){
43384         return this.el;
43385     },
43386
43387     /**
43388      * Hides this region.
43389      */
43390     hide : function(){
43391         //if(!this.collapsed){
43392             this.el.dom.style.left = "-2000px";
43393             this.el.hide();
43394         //}else{
43395          //   this.collapsedEl.dom.style.left = "-2000px";
43396          //   this.collapsedEl.hide();
43397        // }
43398         this.visible = false;
43399         this.fireEvent("visibilitychange", this, false);
43400     },
43401
43402     /**
43403      * Shows this region if it was previously hidden.
43404      */
43405     show : function(){
43406         //if(!this.collapsed){
43407             this.el.show();
43408         //}else{
43409         //    this.collapsedEl.show();
43410        // }
43411         this.visible = true;
43412         this.fireEvent("visibilitychange", this, true);
43413     },
43414 /*
43415     closeClicked : function(){
43416         if(this.activePanel){
43417             this.remove(this.activePanel);
43418         }
43419     },
43420
43421     collapseClick : function(e){
43422         if(this.isSlid){
43423            e.stopPropagation();
43424            this.slideIn();
43425         }else{
43426            e.stopPropagation();
43427            this.slideOut();
43428         }
43429     },
43430 */
43431     /**
43432      * Collapses this region.
43433      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43434      */
43435     /*
43436     collapse : function(skipAnim, skipCheck = false){
43437         if(this.collapsed) {
43438             return;
43439         }
43440         
43441         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43442             
43443             this.collapsed = true;
43444             if(this.split){
43445                 this.split.el.hide();
43446             }
43447             if(this.config.animate && skipAnim !== true){
43448                 this.fireEvent("invalidated", this);
43449                 this.animateCollapse();
43450             }else{
43451                 this.el.setLocation(-20000,-20000);
43452                 this.el.hide();
43453                 this.collapsedEl.show();
43454                 this.fireEvent("collapsed", this);
43455                 this.fireEvent("invalidated", this);
43456             }
43457         }
43458         
43459     },
43460 */
43461     animateCollapse : function(){
43462         // overridden
43463     },
43464
43465     /**
43466      * Expands this region if it was previously collapsed.
43467      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43468      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43469      */
43470     /*
43471     expand : function(e, skipAnim){
43472         if(e) {
43473             e.stopPropagation();
43474         }
43475         if(!this.collapsed || this.el.hasActiveFx()) {
43476             return;
43477         }
43478         if(this.isSlid){
43479             this.afterSlideIn();
43480             skipAnim = true;
43481         }
43482         this.collapsed = false;
43483         if(this.config.animate && skipAnim !== true){
43484             this.animateExpand();
43485         }else{
43486             this.el.show();
43487             if(this.split){
43488                 this.split.el.show();
43489             }
43490             this.collapsedEl.setLocation(-2000,-2000);
43491             this.collapsedEl.hide();
43492             this.fireEvent("invalidated", this);
43493             this.fireEvent("expanded", this);
43494         }
43495     },
43496 */
43497     animateExpand : function(){
43498         // overridden
43499     },
43500
43501     initTabs : function()
43502     {
43503         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43504         
43505         var ts = new Roo.bootstrap.panel.Tabs({
43506             el: this.bodyEl.dom,
43507             region : this,
43508             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
43509             disableTooltips: this.config.disableTabTips,
43510             toolbar : this.config.toolbar
43511         });
43512         
43513         if(this.config.hideTabs){
43514             ts.stripWrap.setDisplayed(false);
43515         }
43516         this.tabs = ts;
43517         ts.resizeTabs = this.config.resizeTabs === true;
43518         ts.minTabWidth = this.config.minTabWidth || 40;
43519         ts.maxTabWidth = this.config.maxTabWidth || 250;
43520         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43521         ts.monitorResize = false;
43522         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43523         ts.bodyEl.addClass('roo-layout-tabs-body');
43524         this.panels.each(this.initPanelAsTab, this);
43525     },
43526
43527     initPanelAsTab : function(panel){
43528         var ti = this.tabs.addTab(
43529             panel.getEl().id,
43530             panel.getTitle(),
43531             null,
43532             this.config.closeOnTab && panel.isClosable(),
43533             panel.tpl
43534         );
43535         if(panel.tabTip !== undefined){
43536             ti.setTooltip(panel.tabTip);
43537         }
43538         ti.on("activate", function(){
43539               this.setActivePanel(panel);
43540         }, this);
43541         
43542         if(this.config.closeOnTab){
43543             ti.on("beforeclose", function(t, e){
43544                 e.cancel = true;
43545                 this.remove(panel);
43546             }, this);
43547         }
43548         
43549         panel.tabItem = ti;
43550         
43551         return ti;
43552     },
43553
43554     updatePanelTitle : function(panel, title)
43555     {
43556         if(this.activePanel == panel){
43557             this.updateTitle(title);
43558         }
43559         if(this.tabs){
43560             var ti = this.tabs.getTab(panel.getEl().id);
43561             ti.setText(title);
43562             if(panel.tabTip !== undefined){
43563                 ti.setTooltip(panel.tabTip);
43564             }
43565         }
43566     },
43567
43568     updateTitle : function(title){
43569         if(this.titleTextEl && !this.config.title){
43570             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43571         }
43572     },
43573
43574     setActivePanel : function(panel)
43575     {
43576         panel = this.getPanel(panel);
43577         if(this.activePanel && this.activePanel != panel){
43578             if(this.activePanel.setActiveState(false) === false){
43579                 return;
43580             }
43581         }
43582         this.activePanel = panel;
43583         panel.setActiveState(true);
43584         if(this.panelSize){
43585             panel.setSize(this.panelSize.width, this.panelSize.height);
43586         }
43587         if(this.closeBtn){
43588             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43589         }
43590         this.updateTitle(panel.getTitle());
43591         if(this.tabs){
43592             this.fireEvent("invalidated", this);
43593         }
43594         this.fireEvent("panelactivated", this, panel);
43595     },
43596
43597     /**
43598      * Shows the specified panel.
43599      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43600      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43601      */
43602     showPanel : function(panel)
43603     {
43604         panel = this.getPanel(panel);
43605         if(panel){
43606             if(this.tabs){
43607                 var tab = this.tabs.getTab(panel.getEl().id);
43608                 if(tab.isHidden()){
43609                     this.tabs.unhideTab(tab.id);
43610                 }
43611                 tab.activate();
43612             }else{
43613                 this.setActivePanel(panel);
43614             }
43615         }
43616         return panel;
43617     },
43618
43619     /**
43620      * Get the active panel for this region.
43621      * @return {Roo.ContentPanel} The active panel or null
43622      */
43623     getActivePanel : function(){
43624         return this.activePanel;
43625     },
43626
43627     validateVisibility : function(){
43628         if(this.panels.getCount() < 1){
43629             this.updateTitle("&#160;");
43630             this.closeBtn.hide();
43631             this.hide();
43632         }else{
43633             if(!this.isVisible()){
43634                 this.show();
43635             }
43636         }
43637     },
43638
43639     /**
43640      * Adds the passed ContentPanel(s) to this region.
43641      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43642      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43643      */
43644     add : function(panel)
43645     {
43646         if(arguments.length > 1){
43647             for(var i = 0, len = arguments.length; i < len; i++) {
43648                 this.add(arguments[i]);
43649             }
43650             return null;
43651         }
43652         
43653         // if we have not been rendered yet, then we can not really do much of this..
43654         if (!this.bodyEl) {
43655             this.unrendered_panels.push(panel);
43656             return panel;
43657         }
43658         
43659         
43660         
43661         
43662         if(this.hasPanel(panel)){
43663             this.showPanel(panel);
43664             return panel;
43665         }
43666         panel.setRegion(this);
43667         this.panels.add(panel);
43668        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43669             // sinle panel - no tab...?? would it not be better to render it with the tabs,
43670             // and hide them... ???
43671             this.bodyEl.dom.appendChild(panel.getEl().dom);
43672             if(panel.background !== true){
43673                 this.setActivePanel(panel);
43674             }
43675             this.fireEvent("paneladded", this, panel);
43676             return panel;
43677         }
43678         */
43679         if(!this.tabs){
43680             this.initTabs();
43681         }else{
43682             this.initPanelAsTab(panel);
43683         }
43684         
43685         
43686         if(panel.background !== true){
43687             this.tabs.activate(panel.getEl().id);
43688         }
43689         this.fireEvent("paneladded", this, panel);
43690         return panel;
43691     },
43692
43693     /**
43694      * Hides the tab for the specified panel.
43695      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43696      */
43697     hidePanel : function(panel){
43698         if(this.tabs && (panel = this.getPanel(panel))){
43699             this.tabs.hideTab(panel.getEl().id);
43700         }
43701     },
43702
43703     /**
43704      * Unhides the tab for a previously hidden panel.
43705      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43706      */
43707     unhidePanel : function(panel){
43708         if(this.tabs && (panel = this.getPanel(panel))){
43709             this.tabs.unhideTab(panel.getEl().id);
43710         }
43711     },
43712
43713     clearPanels : function(){
43714         while(this.panels.getCount() > 0){
43715              this.remove(this.panels.first());
43716         }
43717     },
43718
43719     /**
43720      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43721      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43722      * @param {Boolean} preservePanel Overrides the config preservePanel option
43723      * @return {Roo.ContentPanel} The panel that was removed
43724      */
43725     remove : function(panel, preservePanel)
43726     {
43727         panel = this.getPanel(panel);
43728         if(!panel){
43729             return null;
43730         }
43731         var e = {};
43732         this.fireEvent("beforeremove", this, panel, e);
43733         if(e.cancel === true){
43734             return null;
43735         }
43736         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43737         var panelId = panel.getId();
43738         this.panels.removeKey(panelId);
43739         if(preservePanel){
43740             document.body.appendChild(panel.getEl().dom);
43741         }
43742         if(this.tabs){
43743             this.tabs.removeTab(panel.getEl().id);
43744         }else if (!preservePanel){
43745             this.bodyEl.dom.removeChild(panel.getEl().dom);
43746         }
43747         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43748             var p = this.panels.first();
43749             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43750             tempEl.appendChild(p.getEl().dom);
43751             this.bodyEl.update("");
43752             this.bodyEl.dom.appendChild(p.getEl().dom);
43753             tempEl = null;
43754             this.updateTitle(p.getTitle());
43755             this.tabs = null;
43756             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43757             this.setActivePanel(p);
43758         }
43759         panel.setRegion(null);
43760         if(this.activePanel == panel){
43761             this.activePanel = null;
43762         }
43763         if(this.config.autoDestroy !== false && preservePanel !== true){
43764             try{panel.destroy();}catch(e){}
43765         }
43766         this.fireEvent("panelremoved", this, panel);
43767         return panel;
43768     },
43769
43770     /**
43771      * Returns the TabPanel component used by this region
43772      * @return {Roo.TabPanel}
43773      */
43774     getTabs : function(){
43775         return this.tabs;
43776     },
43777
43778     createTool : function(parentEl, className){
43779         var btn = Roo.DomHelper.append(parentEl, {
43780             tag: "div",
43781             cls: "x-layout-tools-button",
43782             children: [ {
43783                 tag: "div",
43784                 cls: "roo-layout-tools-button-inner " + className,
43785                 html: "&#160;"
43786             }]
43787         }, true);
43788         btn.addClassOnOver("roo-layout-tools-button-over");
43789         return btn;
43790     }
43791 });/*
43792  * Based on:
43793  * Ext JS Library 1.1.1
43794  * Copyright(c) 2006-2007, Ext JS, LLC.
43795  *
43796  * Originally Released Under LGPL - original licence link has changed is not relivant.
43797  *
43798  * Fork - LGPL
43799  * <script type="text/javascript">
43800  */
43801  
43802
43803
43804 /**
43805  * @class Roo.SplitLayoutRegion
43806  * @extends Roo.LayoutRegion
43807  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43808  */
43809 Roo.bootstrap.layout.Split = function(config){
43810     this.cursor = config.cursor;
43811     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
43812 };
43813
43814 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
43815 {
43816     splitTip : "Drag to resize.",
43817     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43818     useSplitTips : false,
43819
43820     applyConfig : function(config){
43821         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
43822     },
43823     
43824     onRender : function(ctr,pos) {
43825         
43826         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
43827         if(!this.config.split){
43828             return;
43829         }
43830         if(!this.split){
43831             
43832             var splitEl = Roo.DomHelper.append(ctr.dom,  {
43833                             tag: "div",
43834                             id: this.el.id + "-split",
43835                             cls: "roo-layout-split roo-layout-split-"+this.position,
43836                             html: "&#160;"
43837             });
43838             /** The SplitBar for this region 
43839             * @type Roo.SplitBar */
43840             // does not exist yet...
43841             Roo.log([this.position, this.orientation]);
43842             
43843             this.split = new Roo.bootstrap.SplitBar({
43844                 dragElement : splitEl,
43845                 resizingElement: this.el,
43846                 orientation : this.orientation
43847             });
43848             
43849             this.split.on("moved", this.onSplitMove, this);
43850             this.split.useShim = this.config.useShim === true;
43851             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43852             if(this.useSplitTips){
43853                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43854             }
43855             //if(config.collapsible){
43856             //    this.split.el.on("dblclick", this.collapse,  this);
43857             //}
43858         }
43859         if(typeof this.config.minSize != "undefined"){
43860             this.split.minSize = this.config.minSize;
43861         }
43862         if(typeof this.config.maxSize != "undefined"){
43863             this.split.maxSize = this.config.maxSize;
43864         }
43865         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
43866             this.hideSplitter();
43867         }
43868         
43869     },
43870
43871     getHMaxSize : function(){
43872          var cmax = this.config.maxSize || 10000;
43873          var center = this.mgr.getRegion("center");
43874          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43875     },
43876
43877     getVMaxSize : function(){
43878          var cmax = this.config.maxSize || 10000;
43879          var center = this.mgr.getRegion("center");
43880          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43881     },
43882
43883     onSplitMove : function(split, newSize){
43884         this.fireEvent("resized", this, newSize);
43885     },
43886     
43887     /** 
43888      * Returns the {@link Roo.SplitBar} for this region.
43889      * @return {Roo.SplitBar}
43890      */
43891     getSplitBar : function(){
43892         return this.split;
43893     },
43894     
43895     hide : function(){
43896         this.hideSplitter();
43897         Roo.bootstrap.layout.Split.superclass.hide.call(this);
43898     },
43899
43900     hideSplitter : function(){
43901         if(this.split){
43902             this.split.el.setLocation(-2000,-2000);
43903             this.split.el.hide();
43904         }
43905     },
43906
43907     show : function(){
43908         if(this.split){
43909             this.split.el.show();
43910         }
43911         Roo.bootstrap.layout.Split.superclass.show.call(this);
43912     },
43913     
43914     beforeSlide: function(){
43915         if(Roo.isGecko){// firefox overflow auto bug workaround
43916             this.bodyEl.clip();
43917             if(this.tabs) {
43918                 this.tabs.bodyEl.clip();
43919             }
43920             if(this.activePanel){
43921                 this.activePanel.getEl().clip();
43922                 
43923                 if(this.activePanel.beforeSlide){
43924                     this.activePanel.beforeSlide();
43925                 }
43926             }
43927         }
43928     },
43929     
43930     afterSlide : function(){
43931         if(Roo.isGecko){// firefox overflow auto bug workaround
43932             this.bodyEl.unclip();
43933             if(this.tabs) {
43934                 this.tabs.bodyEl.unclip();
43935             }
43936             if(this.activePanel){
43937                 this.activePanel.getEl().unclip();
43938                 if(this.activePanel.afterSlide){
43939                     this.activePanel.afterSlide();
43940                 }
43941             }
43942         }
43943     },
43944
43945     initAutoHide : function(){
43946         if(this.autoHide !== false){
43947             if(!this.autoHideHd){
43948                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43949                 this.autoHideHd = {
43950                     "mouseout": function(e){
43951                         if(!e.within(this.el, true)){
43952                             st.delay(500);
43953                         }
43954                     },
43955                     "mouseover" : function(e){
43956                         st.cancel();
43957                     },
43958                     scope : this
43959                 };
43960             }
43961             this.el.on(this.autoHideHd);
43962         }
43963     },
43964
43965     clearAutoHide : function(){
43966         if(this.autoHide !== false){
43967             this.el.un("mouseout", this.autoHideHd.mouseout);
43968             this.el.un("mouseover", this.autoHideHd.mouseover);
43969         }
43970     },
43971
43972     clearMonitor : function(){
43973         Roo.get(document).un("click", this.slideInIf, this);
43974     },
43975
43976     // these names are backwards but not changed for compat
43977     slideOut : function(){
43978         if(this.isSlid || this.el.hasActiveFx()){
43979             return;
43980         }
43981         this.isSlid = true;
43982         if(this.collapseBtn){
43983             this.collapseBtn.hide();
43984         }
43985         this.closeBtnState = this.closeBtn.getStyle('display');
43986         this.closeBtn.hide();
43987         if(this.stickBtn){
43988             this.stickBtn.show();
43989         }
43990         this.el.show();
43991         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43992         this.beforeSlide();
43993         this.el.setStyle("z-index", 10001);
43994         this.el.slideIn(this.getSlideAnchor(), {
43995             callback: function(){
43996                 this.afterSlide();
43997                 this.initAutoHide();
43998                 Roo.get(document).on("click", this.slideInIf, this);
43999                 this.fireEvent("slideshow", this);
44000             },
44001             scope: this,
44002             block: true
44003         });
44004     },
44005
44006     afterSlideIn : function(){
44007         this.clearAutoHide();
44008         this.isSlid = false;
44009         this.clearMonitor();
44010         this.el.setStyle("z-index", "");
44011         if(this.collapseBtn){
44012             this.collapseBtn.show();
44013         }
44014         this.closeBtn.setStyle('display', this.closeBtnState);
44015         if(this.stickBtn){
44016             this.stickBtn.hide();
44017         }
44018         this.fireEvent("slidehide", this);
44019     },
44020
44021     slideIn : function(cb){
44022         if(!this.isSlid || this.el.hasActiveFx()){
44023             Roo.callback(cb);
44024             return;
44025         }
44026         this.isSlid = false;
44027         this.beforeSlide();
44028         this.el.slideOut(this.getSlideAnchor(), {
44029             callback: function(){
44030                 this.el.setLeftTop(-10000, -10000);
44031                 this.afterSlide();
44032                 this.afterSlideIn();
44033                 Roo.callback(cb);
44034             },
44035             scope: this,
44036             block: true
44037         });
44038     },
44039     
44040     slideInIf : function(e){
44041         if(!e.within(this.el)){
44042             this.slideIn();
44043         }
44044     },
44045
44046     animateCollapse : function(){
44047         this.beforeSlide();
44048         this.el.setStyle("z-index", 20000);
44049         var anchor = this.getSlideAnchor();
44050         this.el.slideOut(anchor, {
44051             callback : function(){
44052                 this.el.setStyle("z-index", "");
44053                 this.collapsedEl.slideIn(anchor, {duration:.3});
44054                 this.afterSlide();
44055                 this.el.setLocation(-10000,-10000);
44056                 this.el.hide();
44057                 this.fireEvent("collapsed", this);
44058             },
44059             scope: this,
44060             block: true
44061         });
44062     },
44063
44064     animateExpand : function(){
44065         this.beforeSlide();
44066         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44067         this.el.setStyle("z-index", 20000);
44068         this.collapsedEl.hide({
44069             duration:.1
44070         });
44071         this.el.slideIn(this.getSlideAnchor(), {
44072             callback : function(){
44073                 this.el.setStyle("z-index", "");
44074                 this.afterSlide();
44075                 if(this.split){
44076                     this.split.el.show();
44077                 }
44078                 this.fireEvent("invalidated", this);
44079                 this.fireEvent("expanded", this);
44080             },
44081             scope: this,
44082             block: true
44083         });
44084     },
44085
44086     anchors : {
44087         "west" : "left",
44088         "east" : "right",
44089         "north" : "top",
44090         "south" : "bottom"
44091     },
44092
44093     sanchors : {
44094         "west" : "l",
44095         "east" : "r",
44096         "north" : "t",
44097         "south" : "b"
44098     },
44099
44100     canchors : {
44101         "west" : "tl-tr",
44102         "east" : "tr-tl",
44103         "north" : "tl-bl",
44104         "south" : "bl-tl"
44105     },
44106
44107     getAnchor : function(){
44108         return this.anchors[this.position];
44109     },
44110
44111     getCollapseAnchor : function(){
44112         return this.canchors[this.position];
44113     },
44114
44115     getSlideAnchor : function(){
44116         return this.sanchors[this.position];
44117     },
44118
44119     getAlignAdj : function(){
44120         var cm = this.cmargins;
44121         switch(this.position){
44122             case "west":
44123                 return [0, 0];
44124             break;
44125             case "east":
44126                 return [0, 0];
44127             break;
44128             case "north":
44129                 return [0, 0];
44130             break;
44131             case "south":
44132                 return [0, 0];
44133             break;
44134         }
44135     },
44136
44137     getExpandAdj : function(){
44138         var c = this.collapsedEl, cm = this.cmargins;
44139         switch(this.position){
44140             case "west":
44141                 return [-(cm.right+c.getWidth()+cm.left), 0];
44142             break;
44143             case "east":
44144                 return [cm.right+c.getWidth()+cm.left, 0];
44145             break;
44146             case "north":
44147                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44148             break;
44149             case "south":
44150                 return [0, cm.top+cm.bottom+c.getHeight()];
44151             break;
44152         }
44153     }
44154 });/*
44155  * Based on:
44156  * Ext JS Library 1.1.1
44157  * Copyright(c) 2006-2007, Ext JS, LLC.
44158  *
44159  * Originally Released Under LGPL - original licence link has changed is not relivant.
44160  *
44161  * Fork - LGPL
44162  * <script type="text/javascript">
44163  */
44164 /*
44165  * These classes are private internal classes
44166  */
44167 Roo.bootstrap.layout.Center = function(config){
44168     config.region = "center";
44169     Roo.bootstrap.layout.Region.call(this, config);
44170     this.visible = true;
44171     this.minWidth = config.minWidth || 20;
44172     this.minHeight = config.minHeight || 20;
44173 };
44174
44175 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44176     hide : function(){
44177         // center panel can't be hidden
44178     },
44179     
44180     show : function(){
44181         // center panel can't be hidden
44182     },
44183     
44184     getMinWidth: function(){
44185         return this.minWidth;
44186     },
44187     
44188     getMinHeight: function(){
44189         return this.minHeight;
44190     }
44191 });
44192
44193
44194
44195
44196  
44197
44198
44199
44200
44201
44202
44203 Roo.bootstrap.layout.North = function(config)
44204 {
44205     config.region = 'north';
44206     config.cursor = 'n-resize';
44207     
44208     Roo.bootstrap.layout.Split.call(this, config);
44209     
44210     
44211     if(this.split){
44212         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44213         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44214         this.split.el.addClass("roo-layout-split-v");
44215     }
44216     //var size = config.initialSize || config.height;
44217     //if(this.el && typeof size != "undefined"){
44218     //    this.el.setHeight(size);
44219     //}
44220 };
44221 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44222 {
44223     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44224      
44225      
44226     onRender : function(ctr, pos)
44227     {
44228         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44229         var size = this.config.initialSize || this.config.height;
44230         if(this.el && typeof size != "undefined"){
44231             this.el.setHeight(size);
44232         }
44233     
44234     },
44235     
44236     getBox : function(){
44237         if(this.collapsed){
44238             return this.collapsedEl.getBox();
44239         }
44240         var box = this.el.getBox();
44241         if(this.split){
44242             box.height += this.split.el.getHeight();
44243         }
44244         return box;
44245     },
44246     
44247     updateBox : function(box){
44248         if(this.split && !this.collapsed){
44249             box.height -= this.split.el.getHeight();
44250             this.split.el.setLeft(box.x);
44251             this.split.el.setTop(box.y+box.height);
44252             this.split.el.setWidth(box.width);
44253         }
44254         if(this.collapsed){
44255             this.updateBody(box.width, null);
44256         }
44257         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44258     }
44259 });
44260
44261
44262
44263
44264
44265 Roo.bootstrap.layout.South = function(config){
44266     config.region = 'south';
44267     config.cursor = 's-resize';
44268     Roo.bootstrap.layout.Split.call(this, config);
44269     if(this.split){
44270         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44271         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44272         this.split.el.addClass("roo-layout-split-v");
44273     }
44274     
44275 };
44276
44277 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44278     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44279     
44280     onRender : function(ctr, pos)
44281     {
44282         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44283         var size = this.config.initialSize || this.config.height;
44284         if(this.el && typeof size != "undefined"){
44285             this.el.setHeight(size);
44286         }
44287     
44288     },
44289     
44290     getBox : function(){
44291         if(this.collapsed){
44292             return this.collapsedEl.getBox();
44293         }
44294         var box = this.el.getBox();
44295         if(this.split){
44296             var sh = this.split.el.getHeight();
44297             box.height += sh;
44298             box.y -= sh;
44299         }
44300         return box;
44301     },
44302     
44303     updateBox : function(box){
44304         if(this.split && !this.collapsed){
44305             var sh = this.split.el.getHeight();
44306             box.height -= sh;
44307             box.y += sh;
44308             this.split.el.setLeft(box.x);
44309             this.split.el.setTop(box.y-sh);
44310             this.split.el.setWidth(box.width);
44311         }
44312         if(this.collapsed){
44313             this.updateBody(box.width, null);
44314         }
44315         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44316     }
44317 });
44318
44319 Roo.bootstrap.layout.East = function(config){
44320     config.region = "east";
44321     config.cursor = "e-resize";
44322     Roo.bootstrap.layout.Split.call(this, config);
44323     if(this.split){
44324         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44325         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44326         this.split.el.addClass("roo-layout-split-h");
44327     }
44328     
44329 };
44330 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44331     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44332     
44333     onRender : function(ctr, pos)
44334     {
44335         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44336         var size = this.config.initialSize || this.config.width;
44337         if(this.el && typeof size != "undefined"){
44338             this.el.setWidth(size);
44339         }
44340     
44341     },
44342     
44343     getBox : function(){
44344         if(this.collapsed){
44345             return this.collapsedEl.getBox();
44346         }
44347         var box = this.el.getBox();
44348         if(this.split){
44349             var sw = this.split.el.getWidth();
44350             box.width += sw;
44351             box.x -= sw;
44352         }
44353         return box;
44354     },
44355
44356     updateBox : function(box){
44357         if(this.split && !this.collapsed){
44358             var sw = this.split.el.getWidth();
44359             box.width -= sw;
44360             this.split.el.setLeft(box.x);
44361             this.split.el.setTop(box.y);
44362             this.split.el.setHeight(box.height);
44363             box.x += sw;
44364         }
44365         if(this.collapsed){
44366             this.updateBody(null, box.height);
44367         }
44368         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44369     }
44370 });
44371
44372 Roo.bootstrap.layout.West = function(config){
44373     config.region = "west";
44374     config.cursor = "w-resize";
44375     
44376     Roo.bootstrap.layout.Split.call(this, config);
44377     if(this.split){
44378         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44379         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44380         this.split.el.addClass("roo-layout-split-h");
44381     }
44382     
44383 };
44384 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44385     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44386     
44387     onRender: function(ctr, pos)
44388     {
44389         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44390         var size = this.config.initialSize || this.config.width;
44391         if(typeof size != "undefined"){
44392             this.el.setWidth(size);
44393         }
44394     },
44395     
44396     getBox : function(){
44397         if(this.collapsed){
44398             return this.collapsedEl.getBox();
44399         }
44400         var box = this.el.getBox();
44401         if (box.width == 0) {
44402             box.width = this.config.width; // kludge?
44403         }
44404         if(this.split){
44405             box.width += this.split.el.getWidth();
44406         }
44407         return box;
44408     },
44409     
44410     updateBox : function(box){
44411         if(this.split && !this.collapsed){
44412             var sw = this.split.el.getWidth();
44413             box.width -= sw;
44414             this.split.el.setLeft(box.x+box.width);
44415             this.split.el.setTop(box.y);
44416             this.split.el.setHeight(box.height);
44417         }
44418         if(this.collapsed){
44419             this.updateBody(null, box.height);
44420         }
44421         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44422     }
44423 });/*
44424  * Based on:
44425  * Ext JS Library 1.1.1
44426  * Copyright(c) 2006-2007, Ext JS, LLC.
44427  *
44428  * Originally Released Under LGPL - original licence link has changed is not relivant.
44429  *
44430  * Fork - LGPL
44431  * <script type="text/javascript">
44432  */
44433 /**
44434  * @class Roo.bootstrap.paenl.Content
44435  * @extends Roo.util.Observable
44436  * @children Roo.bootstrap.Component
44437  * @parent builder Roo.bootstrap.layout.Border
44438  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44439  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44440  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44441  * @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
44442  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44443  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44444  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44445  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44446  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44447  * @cfg {String} title          The title for this panel
44448  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44449  * @cfg {String} url            Calls {@link #setUrl} with this value
44450  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44451  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44452  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44453  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44454  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44455  * @cfg {Boolean} badges render the badges
44456  * @cfg {String} cls  extra classes to use  
44457  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44458  
44459  * @constructor
44460  * Create a new ContentPanel.
44461  * @param {String/Object} config A string to set only the title or a config object
44462  
44463  */
44464 Roo.bootstrap.panel.Content = function( config){
44465     
44466     this.tpl = config.tpl || false;
44467     
44468     var el = config.el;
44469     var content = config.content;
44470
44471     if(config.autoCreate){ // xtype is available if this is called from factory
44472         el = Roo.id();
44473     }
44474     this.el = Roo.get(el);
44475     if(!this.el && config && config.autoCreate){
44476         if(typeof config.autoCreate == "object"){
44477             if(!config.autoCreate.id){
44478                 config.autoCreate.id = config.id||el;
44479             }
44480             this.el = Roo.DomHelper.append(document.body,
44481                         config.autoCreate, true);
44482         }else{
44483             var elcfg =  {
44484                 tag: "div",
44485                 cls: (config.cls || '') +
44486                     (config.background ? ' bg-' + config.background : '') +
44487                     " roo-layout-inactive-content",
44488                 id: config.id||el
44489             };
44490             if (config.iframe) {
44491                 elcfg.cn = [
44492                     {
44493                         tag : 'iframe',
44494                         style : 'border: 0px',
44495                         src : 'about:blank'
44496                     }
44497                 ];
44498             }
44499               
44500             if (config.html) {
44501                 elcfg.html = config.html;
44502                 
44503             }
44504                         
44505             this.el = Roo.DomHelper.append(document.body, elcfg , true);
44506             if (config.iframe) {
44507                 this.iframeEl = this.el.select('iframe',true).first();
44508             }
44509             
44510         }
44511     } 
44512     this.closable = false;
44513     this.loaded = false;
44514     this.active = false;
44515    
44516       
44517     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44518         
44519         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44520         
44521         this.wrapEl = this.el; //this.el.wrap();
44522         var ti = [];
44523         if (config.toolbar.items) {
44524             ti = config.toolbar.items ;
44525             delete config.toolbar.items ;
44526         }
44527         
44528         var nitems = [];
44529         this.toolbar.render(this.wrapEl, 'before');
44530         for(var i =0;i < ti.length;i++) {
44531           //  Roo.log(['add child', items[i]]);
44532             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44533         }
44534         this.toolbar.items = nitems;
44535         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44536         delete config.toolbar;
44537         
44538     }
44539     /*
44540     // xtype created footer. - not sure if will work as we normally have to render first..
44541     if (this.footer && !this.footer.el && this.footer.xtype) {
44542         if (!this.wrapEl) {
44543             this.wrapEl = this.el.wrap();
44544         }
44545     
44546         this.footer.container = this.wrapEl.createChild();
44547          
44548         this.footer = Roo.factory(this.footer, Roo);
44549         
44550     }
44551     */
44552     
44553      if(typeof config == "string"){
44554         this.title = config;
44555     }else{
44556         Roo.apply(this, config);
44557     }
44558     
44559     if(this.resizeEl){
44560         this.resizeEl = Roo.get(this.resizeEl, true);
44561     }else{
44562         this.resizeEl = this.el;
44563     }
44564     // handle view.xtype
44565     
44566  
44567     
44568     
44569     this.addEvents({
44570         /**
44571          * @event activate
44572          * Fires when this panel is activated. 
44573          * @param {Roo.ContentPanel} this
44574          */
44575         "activate" : true,
44576         /**
44577          * @event deactivate
44578          * Fires when this panel is activated. 
44579          * @param {Roo.ContentPanel} this
44580          */
44581         "deactivate" : true,
44582
44583         /**
44584          * @event resize
44585          * Fires when this panel is resized if fitToFrame is true.
44586          * @param {Roo.ContentPanel} this
44587          * @param {Number} width The width after any component adjustments
44588          * @param {Number} height The height after any component adjustments
44589          */
44590         "resize" : true,
44591         
44592          /**
44593          * @event render
44594          * Fires when this tab is created
44595          * @param {Roo.ContentPanel} this
44596          */
44597         "render" : true,
44598         
44599           /**
44600          * @event scroll
44601          * Fires when this content is scrolled
44602          * @param {Roo.ContentPanel} this
44603          * @param {Event} scrollEvent
44604          */
44605         "scroll" : true
44606         
44607         
44608         
44609     });
44610     
44611
44612     
44613     
44614     if(this.autoScroll && !this.iframe){
44615         this.resizeEl.setStyle("overflow", "auto");
44616         this.resizeEl.on('scroll', this.onScroll, this);
44617     } else {
44618         // fix randome scrolling
44619         //this.el.on('scroll', function() {
44620         //    Roo.log('fix random scolling');
44621         //    this.scrollTo('top',0); 
44622         //});
44623     }
44624     content = content || this.content;
44625     if(content){
44626         this.setContent(content);
44627     }
44628     if(config && config.url){
44629         this.setUrl(this.url, this.params, this.loadOnce);
44630     }
44631     
44632     
44633     
44634     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
44635     
44636     if (this.view && typeof(this.view.xtype) != 'undefined') {
44637         this.view.el = this.el.appendChild(document.createElement("div"));
44638         this.view = Roo.factory(this.view); 
44639         this.view.render  &&  this.view.render(false, '');  
44640     }
44641     
44642     
44643     this.fireEvent('render', this);
44644 };
44645
44646 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
44647     
44648     cls : '',
44649     background : '',
44650     
44651     tabTip : '',
44652     
44653     iframe : false,
44654     iframeEl : false,
44655     
44656     /* Resize Element - use this to work out scroll etc. */
44657     resizeEl : false,
44658     
44659     setRegion : function(region){
44660         this.region = region;
44661         this.setActiveClass(region && !this.background);
44662     },
44663     
44664     
44665     setActiveClass: function(state)
44666     {
44667         if(state){
44668            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
44669            this.el.setStyle('position','relative');
44670         }else{
44671            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
44672            this.el.setStyle('position', 'absolute');
44673         } 
44674     },
44675     
44676     /**
44677      * Returns the toolbar for this Panel if one was configured. 
44678      * @return {Roo.Toolbar} 
44679      */
44680     getToolbar : function(){
44681         return this.toolbar;
44682     },
44683     
44684     setActiveState : function(active)
44685     {
44686         this.active = active;
44687         this.setActiveClass(active);
44688         if(!active){
44689             if(this.fireEvent("deactivate", this) === false){
44690                 return false;
44691             }
44692             return true;
44693         }
44694         this.fireEvent("activate", this);
44695         return true;
44696     },
44697     /**
44698      * Updates this panel's element (not for iframe)
44699      * @param {String} content The new content
44700      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44701     */
44702     setContent : function(content, loadScripts){
44703         if (this.iframe) {
44704             return;
44705         }
44706         
44707         this.el.update(content, loadScripts);
44708     },
44709
44710     ignoreResize : function(w, h)
44711     {
44712         //return false; // always resize?
44713         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44714             return true;
44715         }else{
44716             this.lastSize = {width: w, height: h};
44717             return false;
44718         }
44719     },
44720     /**
44721      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44722      * @return {Roo.UpdateManager} The UpdateManager
44723      */
44724     getUpdateManager : function(){
44725         if (this.iframe) {
44726             return false;
44727         }
44728         return this.el.getUpdateManager();
44729     },
44730      /**
44731      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44732      * Does not work with IFRAME contents
44733      * @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:
44734 <pre><code>
44735 panel.load({
44736     url: "your-url.php",
44737     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44738     callback: yourFunction,
44739     scope: yourObject, //(optional scope)
44740     discardUrl: false,
44741     nocache: false,
44742     text: "Loading...",
44743     timeout: 30,
44744     scripts: false
44745 });
44746 </code></pre>
44747      
44748      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44749      * 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.
44750      * @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}
44751      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44752      * @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.
44753      * @return {Roo.ContentPanel} this
44754      */
44755     load : function(){
44756         
44757         if (this.iframe) {
44758             return this;
44759         }
44760         
44761         var um = this.el.getUpdateManager();
44762         um.update.apply(um, arguments);
44763         return this;
44764     },
44765
44766
44767     /**
44768      * 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.
44769      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44770      * @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)
44771      * @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)
44772      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
44773      */
44774     setUrl : function(url, params, loadOnce){
44775         if (this.iframe) {
44776             this.iframeEl.dom.src = url;
44777             return false;
44778         }
44779         
44780         if(this.refreshDelegate){
44781             this.removeListener("activate", this.refreshDelegate);
44782         }
44783         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44784         this.on("activate", this.refreshDelegate);
44785         return this.el.getUpdateManager();
44786     },
44787     
44788     _handleRefresh : function(url, params, loadOnce){
44789         if(!loadOnce || !this.loaded){
44790             var updater = this.el.getUpdateManager();
44791             updater.update(url, params, this._setLoaded.createDelegate(this));
44792         }
44793     },
44794     
44795     _setLoaded : function(){
44796         this.loaded = true;
44797     }, 
44798     
44799     /**
44800      * Returns this panel's id
44801      * @return {String} 
44802      */
44803     getId : function(){
44804         return this.el.id;
44805     },
44806     
44807     /** 
44808      * Returns this panel's element - used by regiosn to add.
44809      * @return {Roo.Element} 
44810      */
44811     getEl : function(){
44812         return this.wrapEl || this.el;
44813     },
44814     
44815    
44816     
44817     adjustForComponents : function(width, height)
44818     {
44819         //Roo.log('adjustForComponents ');
44820         if(this.resizeEl != this.el){
44821             width -= this.el.getFrameWidth('lr');
44822             height -= this.el.getFrameWidth('tb');
44823         }
44824         if(this.toolbar){
44825             var te = this.toolbar.getEl();
44826             te.setWidth(width);
44827             height -= te.getHeight();
44828         }
44829         if(this.footer){
44830             var te = this.footer.getEl();
44831             te.setWidth(width);
44832             height -= te.getHeight();
44833         }
44834         
44835         
44836         if(this.adjustments){
44837             width += this.adjustments[0];
44838             height += this.adjustments[1];
44839         }
44840         return {"width": width, "height": height};
44841     },
44842     
44843     setSize : function(width, height){
44844         if(this.fitToFrame && !this.ignoreResize(width, height)){
44845             if(this.fitContainer && this.resizeEl != this.el){
44846                 this.el.setSize(width, height);
44847             }
44848             var size = this.adjustForComponents(width, height);
44849             if (this.iframe) {
44850                 this.iframeEl.setSize(width,height);
44851             }
44852             
44853             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44854             this.fireEvent('resize', this, size.width, size.height);
44855             
44856             
44857         }
44858     },
44859     
44860     /**
44861      * Returns this panel's title
44862      * @return {String} 
44863      */
44864     getTitle : function(){
44865         
44866         if (typeof(this.title) != 'object') {
44867             return this.title;
44868         }
44869         
44870         var t = '';
44871         for (var k in this.title) {
44872             if (!this.title.hasOwnProperty(k)) {
44873                 continue;
44874             }
44875             
44876             if (k.indexOf('-') >= 0) {
44877                 var s = k.split('-');
44878                 for (var i = 0; i<s.length; i++) {
44879                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
44880                 }
44881             } else {
44882                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
44883             }
44884         }
44885         return t;
44886     },
44887     
44888     /**
44889      * Set this panel's title
44890      * @param {String} title
44891      */
44892     setTitle : function(title){
44893         this.title = title;
44894         if(this.region){
44895             this.region.updatePanelTitle(this, title);
44896         }
44897     },
44898     
44899     /**
44900      * Returns true is this panel was configured to be closable
44901      * @return {Boolean} 
44902      */
44903     isClosable : function(){
44904         return this.closable;
44905     },
44906     
44907     beforeSlide : function(){
44908         this.el.clip();
44909         this.resizeEl.clip();
44910     },
44911     
44912     afterSlide : function(){
44913         this.el.unclip();
44914         this.resizeEl.unclip();
44915     },
44916     
44917     /**
44918      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44919      *   Will fail silently if the {@link #setUrl} method has not been called.
44920      *   This does not activate the panel, just updates its content.
44921      */
44922     refresh : function(){
44923         if(this.refreshDelegate){
44924            this.loaded = false;
44925            this.refreshDelegate();
44926         }
44927     },
44928     
44929     /**
44930      * Destroys this panel
44931      */
44932     destroy : function(){
44933         this.el.removeAllListeners();
44934         var tempEl = document.createElement("span");
44935         tempEl.appendChild(this.el.dom);
44936         tempEl.innerHTML = "";
44937         this.el.remove();
44938         this.el = null;
44939     },
44940     
44941     /**
44942      * form - if the content panel contains a form - this is a reference to it.
44943      * @type {Roo.form.Form}
44944      */
44945     form : false,
44946     /**
44947      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
44948      *    This contains a reference to it.
44949      * @type {Roo.View}
44950      */
44951     view : false,
44952     
44953       /**
44954      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44955      * <pre><code>
44956
44957 layout.addxtype({
44958        xtype : 'Form',
44959        items: [ .... ]
44960    }
44961 );
44962
44963 </code></pre>
44964      * @param {Object} cfg Xtype definition of item to add.
44965      */
44966     
44967     
44968     getChildContainer: function () {
44969         return this.getEl();
44970     },
44971     
44972     
44973     onScroll : function(e)
44974     {
44975         this.fireEvent('scroll', this, e);
44976     }
44977     
44978     
44979     /*
44980         var  ret = new Roo.factory(cfg);
44981         return ret;
44982         
44983         
44984         // add form..
44985         if (cfg.xtype.match(/^Form$/)) {
44986             
44987             var el;
44988             //if (this.footer) {
44989             //    el = this.footer.container.insertSibling(false, 'before');
44990             //} else {
44991                 el = this.el.createChild();
44992             //}
44993
44994             this.form = new  Roo.form.Form(cfg);
44995             
44996             
44997             if ( this.form.allItems.length) {
44998                 this.form.render(el.dom);
44999             }
45000             return this.form;
45001         }
45002         // should only have one of theses..
45003         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45004             // views.. should not be just added - used named prop 'view''
45005             
45006             cfg.el = this.el.appendChild(document.createElement("div"));
45007             // factory?
45008             
45009             var ret = new Roo.factory(cfg);
45010              
45011              ret.render && ret.render(false, ''); // render blank..
45012             this.view = ret;
45013             return ret;
45014         }
45015         return false;
45016     }
45017     \*/
45018 });
45019  
45020 /**
45021  * @class Roo.bootstrap.panel.Grid
45022  * @extends Roo.bootstrap.panel.Content
45023  * @constructor
45024  * Create a new GridPanel.
45025  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45026  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45027  * @param {Object} config A the config object
45028   
45029  */
45030
45031
45032
45033 Roo.bootstrap.panel.Grid = function(config)
45034 {
45035     
45036       
45037     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45038         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45039
45040     config.el = this.wrapper;
45041     //this.el = this.wrapper;
45042     
45043       if (config.container) {
45044         // ctor'ed from a Border/panel.grid
45045         
45046         
45047         this.wrapper.setStyle("overflow", "hidden");
45048         this.wrapper.addClass('roo-grid-container');
45049
45050     }
45051     
45052     
45053     if(config.toolbar){
45054         var tool_el = this.wrapper.createChild();    
45055         this.toolbar = Roo.factory(config.toolbar);
45056         var ti = [];
45057         if (config.toolbar.items) {
45058             ti = config.toolbar.items ;
45059             delete config.toolbar.items ;
45060         }
45061         
45062         var nitems = [];
45063         this.toolbar.render(tool_el);
45064         for(var i =0;i < ti.length;i++) {
45065           //  Roo.log(['add child', items[i]]);
45066             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45067         }
45068         this.toolbar.items = nitems;
45069         
45070         delete config.toolbar;
45071     }
45072     
45073     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45074     config.grid.scrollBody = true;;
45075     config.grid.monitorWindowResize = false; // turn off autosizing
45076     config.grid.autoHeight = false;
45077     config.grid.autoWidth = false;
45078     
45079     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45080     
45081     if (config.background) {
45082         // render grid on panel activation (if panel background)
45083         this.on('activate', function(gp) {
45084             if (!gp.grid.rendered) {
45085                 gp.grid.render(this.wrapper);
45086                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45087             }
45088         });
45089             
45090     } else {
45091         this.grid.render(this.wrapper);
45092         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45093
45094     }
45095     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45096     // ??? needed ??? config.el = this.wrapper;
45097     
45098     
45099     
45100   
45101     // xtype created footer. - not sure if will work as we normally have to render first..
45102     if (this.footer && !this.footer.el && this.footer.xtype) {
45103         
45104         var ctr = this.grid.getView().getFooterPanel(true);
45105         this.footer.dataSource = this.grid.dataSource;
45106         this.footer = Roo.factory(this.footer, Roo);
45107         this.footer.render(ctr);
45108         
45109     }
45110     
45111     
45112     
45113     
45114      
45115 };
45116
45117 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45118 {
45119   
45120     getId : function(){
45121         return this.grid.id;
45122     },
45123     
45124     /**
45125      * Returns the grid for this panel
45126      * @return {Roo.bootstrap.Table} 
45127      */
45128     getGrid : function(){
45129         return this.grid;    
45130     },
45131     
45132     setSize : function(width, height)
45133     {
45134      
45135         //if(!this.ignoreResize(width, height)){
45136             var grid = this.grid;
45137             var size = this.adjustForComponents(width, height);
45138             // tfoot is not a footer?
45139           
45140             
45141             var gridel = grid.getGridEl();
45142             gridel.setSize(size.width, size.height);
45143             
45144             var tbd = grid.getGridEl().select('tbody', true).first();
45145             var thd = grid.getGridEl().select('thead',true).first();
45146             var tbf= grid.getGridEl().select('tfoot', true).first();
45147
45148             if (tbf) {
45149                 size.height -= tbf.getHeight();
45150             }
45151             if (thd) {
45152                 size.height -= thd.getHeight();
45153             }
45154             
45155             tbd.setSize(size.width, size.height );
45156             // this is for the account management tab -seems to work there.
45157             var thd = grid.getGridEl().select('thead',true).first();
45158             //if (tbd) {
45159             //    tbd.setSize(size.width, size.height - thd.getHeight());
45160             //}
45161              
45162             grid.autoSize();
45163         //}
45164    
45165     },
45166      
45167     
45168     
45169     beforeSlide : function(){
45170         this.grid.getView().scroller.clip();
45171     },
45172     
45173     afterSlide : function(){
45174         this.grid.getView().scroller.unclip();
45175     },
45176     
45177     destroy : function(){
45178         this.grid.destroy();
45179         delete this.grid;
45180         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45181     }
45182 });
45183
45184 /**
45185  * @class Roo.bootstrap.panel.Nest
45186  * @extends Roo.bootstrap.panel.Content
45187  * @constructor
45188  * Create a new Panel, that can contain a layout.Border.
45189  * 
45190  * 
45191  * @param {String/Object} config A string to set only the title or a config object
45192  */
45193 Roo.bootstrap.panel.Nest = function(config)
45194 {
45195     // construct with only one argument..
45196     /* FIXME - implement nicer consturctors
45197     if (layout.layout) {
45198         config = layout;
45199         layout = config.layout;
45200         delete config.layout;
45201     }
45202     if (layout.xtype && !layout.getEl) {
45203         // then layout needs constructing..
45204         layout = Roo.factory(layout, Roo);
45205     }
45206     */
45207     
45208     config.el =  config.layout.getEl();
45209     
45210     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45211     
45212     config.layout.monitorWindowResize = false; // turn off autosizing
45213     this.layout = config.layout;
45214     this.layout.getEl().addClass("roo-layout-nested-layout");
45215     this.layout.parent = this;
45216     
45217     
45218     
45219     
45220 };
45221
45222 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45223     /**
45224     * @cfg {Roo.BorderLayout} layout The layout for this panel
45225     */
45226     layout : false,
45227
45228     setSize : function(width, height){
45229         if(!this.ignoreResize(width, height)){
45230             var size = this.adjustForComponents(width, height);
45231             var el = this.layout.getEl();
45232             if (size.height < 1) {
45233                 el.setWidth(size.width);   
45234             } else {
45235                 el.setSize(size.width, size.height);
45236             }
45237             var touch = el.dom.offsetWidth;
45238             this.layout.layout();
45239             // ie requires a double layout on the first pass
45240             if(Roo.isIE && !this.initialized){
45241                 this.initialized = true;
45242                 this.layout.layout();
45243             }
45244         }
45245     },
45246     
45247     // activate all subpanels if not currently active..
45248     
45249     setActiveState : function(active){
45250         this.active = active;
45251         this.setActiveClass(active);
45252         
45253         if(!active){
45254             this.fireEvent("deactivate", this);
45255             return;
45256         }
45257         
45258         this.fireEvent("activate", this);
45259         // not sure if this should happen before or after..
45260         if (!this.layout) {
45261             return; // should not happen..
45262         }
45263         var reg = false;
45264         for (var r in this.layout.regions) {
45265             reg = this.layout.getRegion(r);
45266             if (reg.getActivePanel()) {
45267                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45268                 reg.setActivePanel(reg.getActivePanel());
45269                 continue;
45270             }
45271             if (!reg.panels.length) {
45272                 continue;
45273             }
45274             reg.showPanel(reg.getPanel(0));
45275         }
45276         
45277         
45278         
45279         
45280     },
45281     
45282     /**
45283      * Returns the nested BorderLayout for this panel
45284      * @return {Roo.BorderLayout} 
45285      */
45286     getLayout : function(){
45287         return this.layout;
45288     },
45289     
45290      /**
45291      * Adds a xtype elements to the layout of the nested panel
45292      * <pre><code>
45293
45294 panel.addxtype({
45295        xtype : 'ContentPanel',
45296        region: 'west',
45297        items: [ .... ]
45298    }
45299 );
45300
45301 panel.addxtype({
45302         xtype : 'NestedLayoutPanel',
45303         region: 'west',
45304         layout: {
45305            center: { },
45306            west: { }   
45307         },
45308         items : [ ... list of content panels or nested layout panels.. ]
45309    }
45310 );
45311 </code></pre>
45312      * @param {Object} cfg Xtype definition of item to add.
45313      */
45314     addxtype : function(cfg) {
45315         return this.layout.addxtype(cfg);
45316     
45317     }
45318 });/*
45319  * Based on:
45320  * Ext JS Library 1.1.1
45321  * Copyright(c) 2006-2007, Ext JS, LLC.
45322  *
45323  * Originally Released Under LGPL - original licence link has changed is not relivant.
45324  *
45325  * Fork - LGPL
45326  * <script type="text/javascript">
45327  */
45328 /**
45329  * @class Roo.TabPanel
45330  * @extends Roo.util.Observable
45331  * A lightweight tab container.
45332  * <br><br>
45333  * Usage:
45334  * <pre><code>
45335 // basic tabs 1, built from existing content
45336 var tabs = new Roo.TabPanel("tabs1");
45337 tabs.addTab("script", "View Script");
45338 tabs.addTab("markup", "View Markup");
45339 tabs.activate("script");
45340
45341 // more advanced tabs, built from javascript
45342 var jtabs = new Roo.TabPanel("jtabs");
45343 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45344
45345 // set up the UpdateManager
45346 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45347 var updater = tab2.getUpdateManager();
45348 updater.setDefaultUrl("ajax1.htm");
45349 tab2.on('activate', updater.refresh, updater, true);
45350
45351 // Use setUrl for Ajax loading
45352 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45353 tab3.setUrl("ajax2.htm", null, true);
45354
45355 // Disabled tab
45356 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45357 tab4.disable();
45358
45359 jtabs.activate("jtabs-1");
45360  * </code></pre>
45361  * @constructor
45362  * Create a new TabPanel.
45363  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45364  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45365  */
45366 Roo.bootstrap.panel.Tabs = function(config){
45367     /**
45368     * The container element for this TabPanel.
45369     * @type Roo.Element
45370     */
45371     this.el = Roo.get(config.el);
45372     delete config.el;
45373     if(config){
45374         if(typeof config == "boolean"){
45375             this.tabPosition = config ? "bottom" : "top";
45376         }else{
45377             Roo.apply(this, config);
45378         }
45379     }
45380     
45381     if(this.tabPosition == "bottom"){
45382         // if tabs are at the bottom = create the body first.
45383         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45384         this.el.addClass("roo-tabs-bottom");
45385     }
45386     // next create the tabs holders
45387     
45388     if (this.tabPosition == "west"){
45389         
45390         var reg = this.region; // fake it..
45391         while (reg) {
45392             if (!reg.mgr.parent) {
45393                 break;
45394             }
45395             reg = reg.mgr.parent.region;
45396         }
45397         Roo.log("got nest?");
45398         Roo.log(reg);
45399         if (reg.mgr.getRegion('west')) {
45400             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45401             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45402             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45403             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45404             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45405         
45406             
45407         }
45408         
45409         
45410     } else {
45411      
45412         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45413         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45414         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45415         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45416     }
45417     
45418     
45419     if(Roo.isIE){
45420         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45421     }
45422     
45423     // finally - if tabs are at the top, then create the body last..
45424     if(this.tabPosition != "bottom"){
45425         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45426          * @type Roo.Element
45427          */
45428         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45429         this.el.addClass("roo-tabs-top");
45430     }
45431     this.items = [];
45432
45433     this.bodyEl.setStyle("position", "relative");
45434
45435     this.active = null;
45436     this.activateDelegate = this.activate.createDelegate(this);
45437
45438     this.addEvents({
45439         /**
45440          * @event tabchange
45441          * Fires when the active tab changes
45442          * @param {Roo.TabPanel} this
45443          * @param {Roo.TabPanelItem} activePanel The new active tab
45444          */
45445         "tabchange": true,
45446         /**
45447          * @event beforetabchange
45448          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45449          * @param {Roo.TabPanel} this
45450          * @param {Object} e Set cancel to true on this object to cancel the tab change
45451          * @param {Roo.TabPanelItem} tab The tab being changed to
45452          */
45453         "beforetabchange" : true
45454     });
45455
45456     Roo.EventManager.onWindowResize(this.onResize, this);
45457     this.cpad = this.el.getPadding("lr");
45458     this.hiddenCount = 0;
45459
45460
45461     // toolbar on the tabbar support...
45462     if (this.toolbar) {
45463         alert("no toolbar support yet");
45464         this.toolbar  = false;
45465         /*
45466         var tcfg = this.toolbar;
45467         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
45468         this.toolbar = new Roo.Toolbar(tcfg);
45469         if (Roo.isSafari) {
45470             var tbl = tcfg.container.child('table', true);
45471             tbl.setAttribute('width', '100%');
45472         }
45473         */
45474         
45475     }
45476    
45477
45478
45479     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45480 };
45481
45482 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45483     /*
45484      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45485      */
45486     tabPosition : "top",
45487     /*
45488      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45489      */
45490     currentTabWidth : 0,
45491     /*
45492      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45493      */
45494     minTabWidth : 40,
45495     /*
45496      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45497      */
45498     maxTabWidth : 250,
45499     /*
45500      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45501      */
45502     preferredTabWidth : 175,
45503     /*
45504      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45505      */
45506     resizeTabs : false,
45507     /*
45508      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45509      */
45510     monitorResize : true,
45511     /*
45512      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
45513      */
45514     toolbar : false,  // set by caller..
45515     
45516     region : false, /// set by caller
45517     
45518     disableTooltips : true, // not used yet...
45519
45520     /**
45521      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45522      * @param {String} id The id of the div to use <b>or create</b>
45523      * @param {String} text The text for the tab
45524      * @param {String} content (optional) Content to put in the TabPanelItem body
45525      * @param {Boolean} closable (optional) True to create a close icon on the tab
45526      * @return {Roo.TabPanelItem} The created TabPanelItem
45527      */
45528     addTab : function(id, text, content, closable, tpl)
45529     {
45530         var item = new Roo.bootstrap.panel.TabItem({
45531             panel: this,
45532             id : id,
45533             text : text,
45534             closable : closable,
45535             tpl : tpl
45536         });
45537         this.addTabItem(item);
45538         if(content){
45539             item.setContent(content);
45540         }
45541         return item;
45542     },
45543
45544     /**
45545      * Returns the {@link Roo.TabPanelItem} with the specified id/index
45546      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45547      * @return {Roo.TabPanelItem}
45548      */
45549     getTab : function(id){
45550         return this.items[id];
45551     },
45552
45553     /**
45554      * Hides the {@link Roo.TabPanelItem} with the specified id/index
45555      * @param {String/Number} id The id or index of the TabPanelItem to hide.
45556      */
45557     hideTab : function(id){
45558         var t = this.items[id];
45559         if(!t.isHidden()){
45560            t.setHidden(true);
45561            this.hiddenCount++;
45562            this.autoSizeTabs();
45563         }
45564     },
45565
45566     /**
45567      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
45568      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
45569      */
45570     unhideTab : function(id){
45571         var t = this.items[id];
45572         if(t.isHidden()){
45573            t.setHidden(false);
45574            this.hiddenCount--;
45575            this.autoSizeTabs();
45576         }
45577     },
45578
45579     /**
45580      * Adds an existing {@link Roo.TabPanelItem}.
45581      * @param {Roo.TabPanelItem} item The TabPanelItem to add
45582      */
45583     addTabItem : function(item)
45584     {
45585         this.items[item.id] = item;
45586         this.items.push(item);
45587         this.autoSizeTabs();
45588       //  if(this.resizeTabs){
45589     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
45590   //         this.autoSizeTabs();
45591 //        }else{
45592 //            item.autoSize();
45593        // }
45594     },
45595
45596     /**
45597      * Removes a {@link Roo.TabPanelItem}.
45598      * @param {String/Number} id The id or index of the TabPanelItem to remove.
45599      */
45600     removeTab : function(id){
45601         var items = this.items;
45602         var tab = items[id];
45603         if(!tab) { return; }
45604         var index = items.indexOf(tab);
45605         if(this.active == tab && items.length > 1){
45606             var newTab = this.getNextAvailable(index);
45607             if(newTab) {
45608                 newTab.activate();
45609             }
45610         }
45611         this.stripEl.dom.removeChild(tab.pnode.dom);
45612         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
45613             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
45614         }
45615         items.splice(index, 1);
45616         delete this.items[tab.id];
45617         tab.fireEvent("close", tab);
45618         tab.purgeListeners();
45619         this.autoSizeTabs();
45620     },
45621
45622     getNextAvailable : function(start){
45623         var items = this.items;
45624         var index = start;
45625         // look for a next tab that will slide over to
45626         // replace the one being removed
45627         while(index < items.length){
45628             var item = items[++index];
45629             if(item && !item.isHidden()){
45630                 return item;
45631             }
45632         }
45633         // if one isn't found select the previous tab (on the left)
45634         index = start;
45635         while(index >= 0){
45636             var item = items[--index];
45637             if(item && !item.isHidden()){
45638                 return item;
45639             }
45640         }
45641         return null;
45642     },
45643
45644     /**
45645      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
45646      * @param {String/Number} id The id or index of the TabPanelItem to disable.
45647      */
45648     disableTab : function(id){
45649         var tab = this.items[id];
45650         if(tab && this.active != tab){
45651             tab.disable();
45652         }
45653     },
45654
45655     /**
45656      * Enables a {@link Roo.TabPanelItem} that is disabled.
45657      * @param {String/Number} id The id or index of the TabPanelItem to enable.
45658      */
45659     enableTab : function(id){
45660         var tab = this.items[id];
45661         tab.enable();
45662     },
45663
45664     /**
45665      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
45666      * @param {String/Number} id The id or index of the TabPanelItem to activate.
45667      * @return {Roo.TabPanelItem} The TabPanelItem.
45668      */
45669     activate : function(id)
45670     {
45671         //Roo.log('activite:'  + id);
45672         
45673         var tab = this.items[id];
45674         if(!tab){
45675             return null;
45676         }
45677         if(tab == this.active || tab.disabled){
45678             return tab;
45679         }
45680         var e = {};
45681         this.fireEvent("beforetabchange", this, e, tab);
45682         if(e.cancel !== true && !tab.disabled){
45683             if(this.active){
45684                 this.active.hide();
45685             }
45686             this.active = this.items[id];
45687             this.active.show();
45688             this.fireEvent("tabchange", this, this.active);
45689         }
45690         return tab;
45691     },
45692
45693     /**
45694      * Gets the active {@link Roo.TabPanelItem}.
45695      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
45696      */
45697     getActiveTab : function(){
45698         return this.active;
45699     },
45700
45701     /**
45702      * Updates the tab body element to fit the height of the container element
45703      * for overflow scrolling
45704      * @param {Number} targetHeight (optional) Override the starting height from the elements height
45705      */
45706     syncHeight : function(targetHeight){
45707         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45708         var bm = this.bodyEl.getMargins();
45709         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
45710         this.bodyEl.setHeight(newHeight);
45711         return newHeight;
45712     },
45713
45714     onResize : function(){
45715         if(this.monitorResize){
45716             this.autoSizeTabs();
45717         }
45718     },
45719
45720     /**
45721      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
45722      */
45723     beginUpdate : function(){
45724         this.updating = true;
45725     },
45726
45727     /**
45728      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
45729      */
45730     endUpdate : function(){
45731         this.updating = false;
45732         this.autoSizeTabs();
45733     },
45734
45735     /**
45736      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
45737      */
45738     autoSizeTabs : function()
45739     {
45740         var count = this.items.length;
45741         var vcount = count - this.hiddenCount;
45742         
45743         if (vcount < 2) {
45744             this.stripEl.hide();
45745         } else {
45746             this.stripEl.show();
45747         }
45748         
45749         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
45750             return;
45751         }
45752         
45753         
45754         var w = Math.max(this.el.getWidth() - this.cpad, 10);
45755         var availWidth = Math.floor(w / vcount);
45756         var b = this.stripBody;
45757         if(b.getWidth() > w){
45758             var tabs = this.items;
45759             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
45760             if(availWidth < this.minTabWidth){
45761                 /*if(!this.sleft){    // incomplete scrolling code
45762                     this.createScrollButtons();
45763                 }
45764                 this.showScroll();
45765                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
45766             }
45767         }else{
45768             if(this.currentTabWidth < this.preferredTabWidth){
45769                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
45770             }
45771         }
45772     },
45773
45774     /**
45775      * Returns the number of tabs in this TabPanel.
45776      * @return {Number}
45777      */
45778      getCount : function(){
45779          return this.items.length;
45780      },
45781
45782     /**
45783      * Resizes all the tabs to the passed width
45784      * @param {Number} The new width
45785      */
45786     setTabWidth : function(width){
45787         this.currentTabWidth = width;
45788         for(var i = 0, len = this.items.length; i < len; i++) {
45789                 if(!this.items[i].isHidden()) {
45790                 this.items[i].setWidth(width);
45791             }
45792         }
45793     },
45794
45795     /**
45796      * Destroys this TabPanel
45797      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
45798      */
45799     destroy : function(removeEl){
45800         Roo.EventManager.removeResizeListener(this.onResize, this);
45801         for(var i = 0, len = this.items.length; i < len; i++){
45802             this.items[i].purgeListeners();
45803         }
45804         if(removeEl === true){
45805             this.el.update("");
45806             this.el.remove();
45807         }
45808     },
45809     
45810     createStrip : function(container)
45811     {
45812         var strip = document.createElement("nav");
45813         strip.className = Roo.bootstrap.version == 4 ?
45814             "navbar-light bg-light" : 
45815             "navbar navbar-default"; //"x-tabs-wrap";
45816         container.appendChild(strip);
45817         return strip;
45818     },
45819     
45820     createStripList : function(strip)
45821     {
45822         // div wrapper for retard IE
45823         // returns the "tr" element.
45824         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
45825         //'<div class="x-tabs-strip-wrap">'+
45826           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
45827           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
45828         return strip.firstChild; //.firstChild.firstChild.firstChild;
45829     },
45830     createBody : function(container)
45831     {
45832         var body = document.createElement("div");
45833         Roo.id(body, "tab-body");
45834         //Roo.fly(body).addClass("x-tabs-body");
45835         Roo.fly(body).addClass("tab-content");
45836         container.appendChild(body);
45837         return body;
45838     },
45839     createItemBody :function(bodyEl, id){
45840         var body = Roo.getDom(id);
45841         if(!body){
45842             body = document.createElement("div");
45843             body.id = id;
45844         }
45845         //Roo.fly(body).addClass("x-tabs-item-body");
45846         Roo.fly(body).addClass("tab-pane");
45847          bodyEl.insertBefore(body, bodyEl.firstChild);
45848         return body;
45849     },
45850     /** @private */
45851     createStripElements :  function(stripEl, text, closable, tpl)
45852     {
45853         var td = document.createElement("li"); // was td..
45854         td.className = 'nav-item';
45855         
45856         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
45857         
45858         
45859         stripEl.appendChild(td);
45860         /*if(closable){
45861             td.className = "x-tabs-closable";
45862             if(!this.closeTpl){
45863                 this.closeTpl = new Roo.Template(
45864                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45865                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
45866                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
45867                 );
45868             }
45869             var el = this.closeTpl.overwrite(td, {"text": text});
45870             var close = el.getElementsByTagName("div")[0];
45871             var inner = el.getElementsByTagName("em")[0];
45872             return {"el": el, "close": close, "inner": inner};
45873         } else {
45874         */
45875         // not sure what this is..
45876 //            if(!this.tabTpl){
45877                 //this.tabTpl = new Roo.Template(
45878                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45879                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
45880                 //);
45881 //                this.tabTpl = new Roo.Template(
45882 //                   '<a href="#">' +
45883 //                   '<span unselectable="on"' +
45884 //                            (this.disableTooltips ? '' : ' title="{text}"') +
45885 //                            ' >{text}</span></a>'
45886 //                );
45887 //                
45888 //            }
45889
45890
45891             var template = tpl || this.tabTpl || false;
45892             
45893             if(!template){
45894                 template =  new Roo.Template(
45895                         Roo.bootstrap.version == 4 ? 
45896                             (
45897                                 '<a class="nav-link" href="#" unselectable="on"' +
45898                                      (this.disableTooltips ? '' : ' title="{text}"') +
45899                                      ' >{text}</a>'
45900                             ) : (
45901                                 '<a class="nav-link" href="#">' +
45902                                 '<span unselectable="on"' +
45903                                          (this.disableTooltips ? '' : ' title="{text}"') +
45904                                     ' >{text}</span></a>'
45905                             )
45906                 );
45907             }
45908             
45909             switch (typeof(template)) {
45910                 case 'object' :
45911                     break;
45912                 case 'string' :
45913                     template = new Roo.Template(template);
45914                     break;
45915                 default :
45916                     break;
45917             }
45918             
45919             var el = template.overwrite(td, {"text": text});
45920             
45921             var inner = el.getElementsByTagName("span")[0];
45922             
45923             return {"el": el, "inner": inner};
45924             
45925     }
45926         
45927     
45928 });
45929
45930 /**
45931  * @class Roo.TabPanelItem
45932  * @extends Roo.util.Observable
45933  * Represents an individual item (tab plus body) in a TabPanel.
45934  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
45935  * @param {String} id The id of this TabPanelItem
45936  * @param {String} text The text for the tab of this TabPanelItem
45937  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
45938  */
45939 Roo.bootstrap.panel.TabItem = function(config){
45940     /**
45941      * The {@link Roo.TabPanel} this TabPanelItem belongs to
45942      * @type Roo.TabPanel
45943      */
45944     this.tabPanel = config.panel;
45945     /**
45946      * The id for this TabPanelItem
45947      * @type String
45948      */
45949     this.id = config.id;
45950     /** @private */
45951     this.disabled = false;
45952     /** @private */
45953     this.text = config.text;
45954     /** @private */
45955     this.loaded = false;
45956     this.closable = config.closable;
45957
45958     /**
45959      * The body element for this TabPanelItem.
45960      * @type Roo.Element
45961      */
45962     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
45963     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
45964     this.bodyEl.setStyle("display", "block");
45965     this.bodyEl.setStyle("zoom", "1");
45966     //this.hideAction();
45967
45968     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
45969     /** @private */
45970     this.el = Roo.get(els.el);
45971     this.inner = Roo.get(els.inner, true);
45972      this.textEl = Roo.bootstrap.version == 4 ?
45973         this.el : Roo.get(this.el.dom.firstChild, true);
45974
45975     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
45976     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
45977
45978     
45979 //    this.el.on("mousedown", this.onTabMouseDown, this);
45980     this.el.on("click", this.onTabClick, this);
45981     /** @private */
45982     if(config.closable){
45983         var c = Roo.get(els.close, true);
45984         c.dom.title = this.closeText;
45985         c.addClassOnOver("close-over");
45986         c.on("click", this.closeClick, this);
45987      }
45988
45989     this.addEvents({
45990          /**
45991          * @event activate
45992          * Fires when this tab becomes the active tab.
45993          * @param {Roo.TabPanel} tabPanel The parent TabPanel
45994          * @param {Roo.TabPanelItem} this
45995          */
45996         "activate": true,
45997         /**
45998          * @event beforeclose
45999          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46000          * @param {Roo.TabPanelItem} this
46001          * @param {Object} e Set cancel to true on this object to cancel the close.
46002          */
46003         "beforeclose": true,
46004         /**
46005          * @event close
46006          * Fires when this tab is closed.
46007          * @param {Roo.TabPanelItem} this
46008          */
46009          "close": true,
46010         /**
46011          * @event deactivate
46012          * Fires when this tab is no longer the active tab.
46013          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46014          * @param {Roo.TabPanelItem} this
46015          */
46016          "deactivate" : true
46017     });
46018     this.hidden = false;
46019
46020     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46021 };
46022
46023 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46024            {
46025     purgeListeners : function(){
46026        Roo.util.Observable.prototype.purgeListeners.call(this);
46027        this.el.removeAllListeners();
46028     },
46029     /**
46030      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46031      */
46032     show : function(){
46033         this.status_node.addClass("active");
46034         this.showAction();
46035         if(Roo.isOpera){
46036             this.tabPanel.stripWrap.repaint();
46037         }
46038         this.fireEvent("activate", this.tabPanel, this);
46039     },
46040
46041     /**
46042      * Returns true if this tab is the active tab.
46043      * @return {Boolean}
46044      */
46045     isActive : function(){
46046         return this.tabPanel.getActiveTab() == this;
46047     },
46048
46049     /**
46050      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46051      */
46052     hide : function(){
46053         this.status_node.removeClass("active");
46054         this.hideAction();
46055         this.fireEvent("deactivate", this.tabPanel, this);
46056     },
46057
46058     hideAction : function(){
46059         this.bodyEl.hide();
46060         this.bodyEl.setStyle("position", "absolute");
46061         this.bodyEl.setLeft("-20000px");
46062         this.bodyEl.setTop("-20000px");
46063     },
46064
46065     showAction : function(){
46066         this.bodyEl.setStyle("position", "relative");
46067         this.bodyEl.setTop("");
46068         this.bodyEl.setLeft("");
46069         this.bodyEl.show();
46070     },
46071
46072     /**
46073      * Set the tooltip for the tab.
46074      * @param {String} tooltip The tab's tooltip
46075      */
46076     setTooltip : function(text){
46077         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46078             this.textEl.dom.qtip = text;
46079             this.textEl.dom.removeAttribute('title');
46080         }else{
46081             this.textEl.dom.title = text;
46082         }
46083     },
46084
46085     onTabClick : function(e){
46086         e.preventDefault();
46087         this.tabPanel.activate(this.id);
46088     },
46089
46090     onTabMouseDown : function(e){
46091         e.preventDefault();
46092         this.tabPanel.activate(this.id);
46093     },
46094 /*
46095     getWidth : function(){
46096         return this.inner.getWidth();
46097     },
46098
46099     setWidth : function(width){
46100         var iwidth = width - this.linode.getPadding("lr");
46101         this.inner.setWidth(iwidth);
46102         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46103         this.linode.setWidth(width);
46104     },
46105 */
46106     /**
46107      * Show or hide the tab
46108      * @param {Boolean} hidden True to hide or false to show.
46109      */
46110     setHidden : function(hidden){
46111         this.hidden = hidden;
46112         this.linode.setStyle("display", hidden ? "none" : "");
46113     },
46114
46115     /**
46116      * Returns true if this tab is "hidden"
46117      * @return {Boolean}
46118      */
46119     isHidden : function(){
46120         return this.hidden;
46121     },
46122
46123     /**
46124      * Returns the text for this tab
46125      * @return {String}
46126      */
46127     getText : function(){
46128         return this.text;
46129     },
46130     /*
46131     autoSize : function(){
46132         //this.el.beginMeasure();
46133         this.textEl.setWidth(1);
46134         /*
46135          *  #2804 [new] Tabs in Roojs
46136          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46137          */
46138         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46139         //this.el.endMeasure();
46140     //},
46141
46142     /**
46143      * Sets the text for the tab (Note: this also sets the tooltip text)
46144      * @param {String} text The tab's text and tooltip
46145      */
46146     setText : function(text){
46147         this.text = text;
46148         this.textEl.update(text);
46149         this.setTooltip(text);
46150         //if(!this.tabPanel.resizeTabs){
46151         //    this.autoSize();
46152         //}
46153     },
46154     /**
46155      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46156      */
46157     activate : function(){
46158         this.tabPanel.activate(this.id);
46159     },
46160
46161     /**
46162      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46163      */
46164     disable : function(){
46165         if(this.tabPanel.active != this){
46166             this.disabled = true;
46167             this.status_node.addClass("disabled");
46168         }
46169     },
46170
46171     /**
46172      * Enables this TabPanelItem if it was previously disabled.
46173      */
46174     enable : function(){
46175         this.disabled = false;
46176         this.status_node.removeClass("disabled");
46177     },
46178
46179     /**
46180      * Sets the content for this TabPanelItem.
46181      * @param {String} content The content
46182      * @param {Boolean} loadScripts true to look for and load scripts
46183      */
46184     setContent : function(content, loadScripts){
46185         this.bodyEl.update(content, loadScripts);
46186     },
46187
46188     /**
46189      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46190      * @return {Roo.UpdateManager} The UpdateManager
46191      */
46192     getUpdateManager : function(){
46193         return this.bodyEl.getUpdateManager();
46194     },
46195
46196     /**
46197      * Set a URL to be used to load the content for this TabPanelItem.
46198      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46199      * @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)
46200      * @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)
46201      * @return {Roo.UpdateManager} The UpdateManager
46202      */
46203     setUrl : function(url, params, loadOnce){
46204         if(this.refreshDelegate){
46205             this.un('activate', this.refreshDelegate);
46206         }
46207         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46208         this.on("activate", this.refreshDelegate);
46209         return this.bodyEl.getUpdateManager();
46210     },
46211
46212     /** @private */
46213     _handleRefresh : function(url, params, loadOnce){
46214         if(!loadOnce || !this.loaded){
46215             var updater = this.bodyEl.getUpdateManager();
46216             updater.update(url, params, this._setLoaded.createDelegate(this));
46217         }
46218     },
46219
46220     /**
46221      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46222      *   Will fail silently if the setUrl method has not been called.
46223      *   This does not activate the panel, just updates its content.
46224      */
46225     refresh : function(){
46226         if(this.refreshDelegate){
46227            this.loaded = false;
46228            this.refreshDelegate();
46229         }
46230     },
46231
46232     /** @private */
46233     _setLoaded : function(){
46234         this.loaded = true;
46235     },
46236
46237     /** @private */
46238     closeClick : function(e){
46239         var o = {};
46240         e.stopEvent();
46241         this.fireEvent("beforeclose", this, o);
46242         if(o.cancel !== true){
46243             this.tabPanel.removeTab(this.id);
46244         }
46245     },
46246     /**
46247      * The text displayed in the tooltip for the close icon.
46248      * @type String
46249      */
46250     closeText : "Close this tab"
46251 });
46252 /**
46253 *    This script refer to:
46254 *    Title: International Telephone Input
46255 *    Author: Jack O'Connor
46256 *    Code version:  v12.1.12
46257 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46258 **/
46259
46260 Roo.bootstrap.form.PhoneInputData = function() {
46261     var d = [
46262       [
46263         "Afghanistan (‫افغانستان‬‎)",
46264         "af",
46265         "93"
46266       ],
46267       [
46268         "Albania (Shqipëri)",
46269         "al",
46270         "355"
46271       ],
46272       [
46273         "Algeria (‫الجزائر‬‎)",
46274         "dz",
46275         "213"
46276       ],
46277       [
46278         "American Samoa",
46279         "as",
46280         "1684"
46281       ],
46282       [
46283         "Andorra",
46284         "ad",
46285         "376"
46286       ],
46287       [
46288         "Angola",
46289         "ao",
46290         "244"
46291       ],
46292       [
46293         "Anguilla",
46294         "ai",
46295         "1264"
46296       ],
46297       [
46298         "Antigua and Barbuda",
46299         "ag",
46300         "1268"
46301       ],
46302       [
46303         "Argentina",
46304         "ar",
46305         "54"
46306       ],
46307       [
46308         "Armenia (Հայաստան)",
46309         "am",
46310         "374"
46311       ],
46312       [
46313         "Aruba",
46314         "aw",
46315         "297"
46316       ],
46317       [
46318         "Australia",
46319         "au",
46320         "61",
46321         0
46322       ],
46323       [
46324         "Austria (Österreich)",
46325         "at",
46326         "43"
46327       ],
46328       [
46329         "Azerbaijan (Azərbaycan)",
46330         "az",
46331         "994"
46332       ],
46333       [
46334         "Bahamas",
46335         "bs",
46336         "1242"
46337       ],
46338       [
46339         "Bahrain (‫البحرين‬‎)",
46340         "bh",
46341         "973"
46342       ],
46343       [
46344         "Bangladesh (বাংলাদেশ)",
46345         "bd",
46346         "880"
46347       ],
46348       [
46349         "Barbados",
46350         "bb",
46351         "1246"
46352       ],
46353       [
46354         "Belarus (Беларусь)",
46355         "by",
46356         "375"
46357       ],
46358       [
46359         "Belgium (België)",
46360         "be",
46361         "32"
46362       ],
46363       [
46364         "Belize",
46365         "bz",
46366         "501"
46367       ],
46368       [
46369         "Benin (Bénin)",
46370         "bj",
46371         "229"
46372       ],
46373       [
46374         "Bermuda",
46375         "bm",
46376         "1441"
46377       ],
46378       [
46379         "Bhutan (འབྲུག)",
46380         "bt",
46381         "975"
46382       ],
46383       [
46384         "Bolivia",
46385         "bo",
46386         "591"
46387       ],
46388       [
46389         "Bosnia and Herzegovina (Босна и Херцеговина)",
46390         "ba",
46391         "387"
46392       ],
46393       [
46394         "Botswana",
46395         "bw",
46396         "267"
46397       ],
46398       [
46399         "Brazil (Brasil)",
46400         "br",
46401         "55"
46402       ],
46403       [
46404         "British Indian Ocean Territory",
46405         "io",
46406         "246"
46407       ],
46408       [
46409         "British Virgin Islands",
46410         "vg",
46411         "1284"
46412       ],
46413       [
46414         "Brunei",
46415         "bn",
46416         "673"
46417       ],
46418       [
46419         "Bulgaria (България)",
46420         "bg",
46421         "359"
46422       ],
46423       [
46424         "Burkina Faso",
46425         "bf",
46426         "226"
46427       ],
46428       [
46429         "Burundi (Uburundi)",
46430         "bi",
46431         "257"
46432       ],
46433       [
46434         "Cambodia (កម្ពុជា)",
46435         "kh",
46436         "855"
46437       ],
46438       [
46439         "Cameroon (Cameroun)",
46440         "cm",
46441         "237"
46442       ],
46443       [
46444         "Canada",
46445         "ca",
46446         "1",
46447         1,
46448         ["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"]
46449       ],
46450       [
46451         "Cape Verde (Kabu Verdi)",
46452         "cv",
46453         "238"
46454       ],
46455       [
46456         "Caribbean Netherlands",
46457         "bq",
46458         "599",
46459         1
46460       ],
46461       [
46462         "Cayman Islands",
46463         "ky",
46464         "1345"
46465       ],
46466       [
46467         "Central African Republic (République centrafricaine)",
46468         "cf",
46469         "236"
46470       ],
46471       [
46472         "Chad (Tchad)",
46473         "td",
46474         "235"
46475       ],
46476       [
46477         "Chile",
46478         "cl",
46479         "56"
46480       ],
46481       [
46482         "China (中国)",
46483         "cn",
46484         "86"
46485       ],
46486       [
46487         "Christmas Island",
46488         "cx",
46489         "61",
46490         2
46491       ],
46492       [
46493         "Cocos (Keeling) Islands",
46494         "cc",
46495         "61",
46496         1
46497       ],
46498       [
46499         "Colombia",
46500         "co",
46501         "57"
46502       ],
46503       [
46504         "Comoros (‫جزر القمر‬‎)",
46505         "km",
46506         "269"
46507       ],
46508       [
46509         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46510         "cd",
46511         "243"
46512       ],
46513       [
46514         "Congo (Republic) (Congo-Brazzaville)",
46515         "cg",
46516         "242"
46517       ],
46518       [
46519         "Cook Islands",
46520         "ck",
46521         "682"
46522       ],
46523       [
46524         "Costa Rica",
46525         "cr",
46526         "506"
46527       ],
46528       [
46529         "Côte d’Ivoire",
46530         "ci",
46531         "225"
46532       ],
46533       [
46534         "Croatia (Hrvatska)",
46535         "hr",
46536         "385"
46537       ],
46538       [
46539         "Cuba",
46540         "cu",
46541         "53"
46542       ],
46543       [
46544         "Curaçao",
46545         "cw",
46546         "599",
46547         0
46548       ],
46549       [
46550         "Cyprus (Κύπρος)",
46551         "cy",
46552         "357"
46553       ],
46554       [
46555         "Czech Republic (Česká republika)",
46556         "cz",
46557         "420"
46558       ],
46559       [
46560         "Denmark (Danmark)",
46561         "dk",
46562         "45"
46563       ],
46564       [
46565         "Djibouti",
46566         "dj",
46567         "253"
46568       ],
46569       [
46570         "Dominica",
46571         "dm",
46572         "1767"
46573       ],
46574       [
46575         "Dominican Republic (República Dominicana)",
46576         "do",
46577         "1",
46578         2,
46579         ["809", "829", "849"]
46580       ],
46581       [
46582         "Ecuador",
46583         "ec",
46584         "593"
46585       ],
46586       [
46587         "Egypt (‫مصر‬‎)",
46588         "eg",
46589         "20"
46590       ],
46591       [
46592         "El Salvador",
46593         "sv",
46594         "503"
46595       ],
46596       [
46597         "Equatorial Guinea (Guinea Ecuatorial)",
46598         "gq",
46599         "240"
46600       ],
46601       [
46602         "Eritrea",
46603         "er",
46604         "291"
46605       ],
46606       [
46607         "Estonia (Eesti)",
46608         "ee",
46609         "372"
46610       ],
46611       [
46612         "Ethiopia",
46613         "et",
46614         "251"
46615       ],
46616       [
46617         "Falkland Islands (Islas Malvinas)",
46618         "fk",
46619         "500"
46620       ],
46621       [
46622         "Faroe Islands (Føroyar)",
46623         "fo",
46624         "298"
46625       ],
46626       [
46627         "Fiji",
46628         "fj",
46629         "679"
46630       ],
46631       [
46632         "Finland (Suomi)",
46633         "fi",
46634         "358",
46635         0
46636       ],
46637       [
46638         "France",
46639         "fr",
46640         "33"
46641       ],
46642       [
46643         "French Guiana (Guyane française)",
46644         "gf",
46645         "594"
46646       ],
46647       [
46648         "French Polynesia (Polynésie française)",
46649         "pf",
46650         "689"
46651       ],
46652       [
46653         "Gabon",
46654         "ga",
46655         "241"
46656       ],
46657       [
46658         "Gambia",
46659         "gm",
46660         "220"
46661       ],
46662       [
46663         "Georgia (საქართველო)",
46664         "ge",
46665         "995"
46666       ],
46667       [
46668         "Germany (Deutschland)",
46669         "de",
46670         "49"
46671       ],
46672       [
46673         "Ghana (Gaana)",
46674         "gh",
46675         "233"
46676       ],
46677       [
46678         "Gibraltar",
46679         "gi",
46680         "350"
46681       ],
46682       [
46683         "Greece (Ελλάδα)",
46684         "gr",
46685         "30"
46686       ],
46687       [
46688         "Greenland (Kalaallit Nunaat)",
46689         "gl",
46690         "299"
46691       ],
46692       [
46693         "Grenada",
46694         "gd",
46695         "1473"
46696       ],
46697       [
46698         "Guadeloupe",
46699         "gp",
46700         "590",
46701         0
46702       ],
46703       [
46704         "Guam",
46705         "gu",
46706         "1671"
46707       ],
46708       [
46709         "Guatemala",
46710         "gt",
46711         "502"
46712       ],
46713       [
46714         "Guernsey",
46715         "gg",
46716         "44",
46717         1
46718       ],
46719       [
46720         "Guinea (Guinée)",
46721         "gn",
46722         "224"
46723       ],
46724       [
46725         "Guinea-Bissau (Guiné Bissau)",
46726         "gw",
46727         "245"
46728       ],
46729       [
46730         "Guyana",
46731         "gy",
46732         "592"
46733       ],
46734       [
46735         "Haiti",
46736         "ht",
46737         "509"
46738       ],
46739       [
46740         "Honduras",
46741         "hn",
46742         "504"
46743       ],
46744       [
46745         "Hong Kong (香港)",
46746         "hk",
46747         "852"
46748       ],
46749       [
46750         "Hungary (Magyarország)",
46751         "hu",
46752         "36"
46753       ],
46754       [
46755         "Iceland (Ísland)",
46756         "is",
46757         "354"
46758       ],
46759       [
46760         "India (भारत)",
46761         "in",
46762         "91"
46763       ],
46764       [
46765         "Indonesia",
46766         "id",
46767         "62"
46768       ],
46769       [
46770         "Iran (‫ایران‬‎)",
46771         "ir",
46772         "98"
46773       ],
46774       [
46775         "Iraq (‫العراق‬‎)",
46776         "iq",
46777         "964"
46778       ],
46779       [
46780         "Ireland",
46781         "ie",
46782         "353"
46783       ],
46784       [
46785         "Isle of Man",
46786         "im",
46787         "44",
46788         2
46789       ],
46790       [
46791         "Israel (‫ישראל‬‎)",
46792         "il",
46793         "972"
46794       ],
46795       [
46796         "Italy (Italia)",
46797         "it",
46798         "39",
46799         0
46800       ],
46801       [
46802         "Jamaica",
46803         "jm",
46804         "1876"
46805       ],
46806       [
46807         "Japan (日本)",
46808         "jp",
46809         "81"
46810       ],
46811       [
46812         "Jersey",
46813         "je",
46814         "44",
46815         3
46816       ],
46817       [
46818         "Jordan (‫الأردن‬‎)",
46819         "jo",
46820         "962"
46821       ],
46822       [
46823         "Kazakhstan (Казахстан)",
46824         "kz",
46825         "7",
46826         1
46827       ],
46828       [
46829         "Kenya",
46830         "ke",
46831         "254"
46832       ],
46833       [
46834         "Kiribati",
46835         "ki",
46836         "686"
46837       ],
46838       [
46839         "Kosovo",
46840         "xk",
46841         "383"
46842       ],
46843       [
46844         "Kuwait (‫الكويت‬‎)",
46845         "kw",
46846         "965"
46847       ],
46848       [
46849         "Kyrgyzstan (Кыргызстан)",
46850         "kg",
46851         "996"
46852       ],
46853       [
46854         "Laos (ລາວ)",
46855         "la",
46856         "856"
46857       ],
46858       [
46859         "Latvia (Latvija)",
46860         "lv",
46861         "371"
46862       ],
46863       [
46864         "Lebanon (‫لبنان‬‎)",
46865         "lb",
46866         "961"
46867       ],
46868       [
46869         "Lesotho",
46870         "ls",
46871         "266"
46872       ],
46873       [
46874         "Liberia",
46875         "lr",
46876         "231"
46877       ],
46878       [
46879         "Libya (‫ليبيا‬‎)",
46880         "ly",
46881         "218"
46882       ],
46883       [
46884         "Liechtenstein",
46885         "li",
46886         "423"
46887       ],
46888       [
46889         "Lithuania (Lietuva)",
46890         "lt",
46891         "370"
46892       ],
46893       [
46894         "Luxembourg",
46895         "lu",
46896         "352"
46897       ],
46898       [
46899         "Macau (澳門)",
46900         "mo",
46901         "853"
46902       ],
46903       [
46904         "Macedonia (FYROM) (Македонија)",
46905         "mk",
46906         "389"
46907       ],
46908       [
46909         "Madagascar (Madagasikara)",
46910         "mg",
46911         "261"
46912       ],
46913       [
46914         "Malawi",
46915         "mw",
46916         "265"
46917       ],
46918       [
46919         "Malaysia",
46920         "my",
46921         "60"
46922       ],
46923       [
46924         "Maldives",
46925         "mv",
46926         "960"
46927       ],
46928       [
46929         "Mali",
46930         "ml",
46931         "223"
46932       ],
46933       [
46934         "Malta",
46935         "mt",
46936         "356"
46937       ],
46938       [
46939         "Marshall Islands",
46940         "mh",
46941         "692"
46942       ],
46943       [
46944         "Martinique",
46945         "mq",
46946         "596"
46947       ],
46948       [
46949         "Mauritania (‫موريتانيا‬‎)",
46950         "mr",
46951         "222"
46952       ],
46953       [
46954         "Mauritius (Moris)",
46955         "mu",
46956         "230"
46957       ],
46958       [
46959         "Mayotte",
46960         "yt",
46961         "262",
46962         1
46963       ],
46964       [
46965         "Mexico (México)",
46966         "mx",
46967         "52"
46968       ],
46969       [
46970         "Micronesia",
46971         "fm",
46972         "691"
46973       ],
46974       [
46975         "Moldova (Republica Moldova)",
46976         "md",
46977         "373"
46978       ],
46979       [
46980         "Monaco",
46981         "mc",
46982         "377"
46983       ],
46984       [
46985         "Mongolia (Монгол)",
46986         "mn",
46987         "976"
46988       ],
46989       [
46990         "Montenegro (Crna Gora)",
46991         "me",
46992         "382"
46993       ],
46994       [
46995         "Montserrat",
46996         "ms",
46997         "1664"
46998       ],
46999       [
47000         "Morocco (‫المغرب‬‎)",
47001         "ma",
47002         "212",
47003         0
47004       ],
47005       [
47006         "Mozambique (Moçambique)",
47007         "mz",
47008         "258"
47009       ],
47010       [
47011         "Myanmar (Burma) (မြန်မာ)",
47012         "mm",
47013         "95"
47014       ],
47015       [
47016         "Namibia (Namibië)",
47017         "na",
47018         "264"
47019       ],
47020       [
47021         "Nauru",
47022         "nr",
47023         "674"
47024       ],
47025       [
47026         "Nepal (नेपाल)",
47027         "np",
47028         "977"
47029       ],
47030       [
47031         "Netherlands (Nederland)",
47032         "nl",
47033         "31"
47034       ],
47035       [
47036         "New Caledonia (Nouvelle-Calédonie)",
47037         "nc",
47038         "687"
47039       ],
47040       [
47041         "New Zealand",
47042         "nz",
47043         "64"
47044       ],
47045       [
47046         "Nicaragua",
47047         "ni",
47048         "505"
47049       ],
47050       [
47051         "Niger (Nijar)",
47052         "ne",
47053         "227"
47054       ],
47055       [
47056         "Nigeria",
47057         "ng",
47058         "234"
47059       ],
47060       [
47061         "Niue",
47062         "nu",
47063         "683"
47064       ],
47065       [
47066         "Norfolk Island",
47067         "nf",
47068         "672"
47069       ],
47070       [
47071         "North Korea (조선 민주주의 인민 공화국)",
47072         "kp",
47073         "850"
47074       ],
47075       [
47076         "Northern Mariana Islands",
47077         "mp",
47078         "1670"
47079       ],
47080       [
47081         "Norway (Norge)",
47082         "no",
47083         "47",
47084         0
47085       ],
47086       [
47087         "Oman (‫عُمان‬‎)",
47088         "om",
47089         "968"
47090       ],
47091       [
47092         "Pakistan (‫پاکستان‬‎)",
47093         "pk",
47094         "92"
47095       ],
47096       [
47097         "Palau",
47098         "pw",
47099         "680"
47100       ],
47101       [
47102         "Palestine (‫فلسطين‬‎)",
47103         "ps",
47104         "970"
47105       ],
47106       [
47107         "Panama (Panamá)",
47108         "pa",
47109         "507"
47110       ],
47111       [
47112         "Papua New Guinea",
47113         "pg",
47114         "675"
47115       ],
47116       [
47117         "Paraguay",
47118         "py",
47119         "595"
47120       ],
47121       [
47122         "Peru (Perú)",
47123         "pe",
47124         "51"
47125       ],
47126       [
47127         "Philippines",
47128         "ph",
47129         "63"
47130       ],
47131       [
47132         "Poland (Polska)",
47133         "pl",
47134         "48"
47135       ],
47136       [
47137         "Portugal",
47138         "pt",
47139         "351"
47140       ],
47141       [
47142         "Puerto Rico",
47143         "pr",
47144         "1",
47145         3,
47146         ["787", "939"]
47147       ],
47148       [
47149         "Qatar (‫قطر‬‎)",
47150         "qa",
47151         "974"
47152       ],
47153       [
47154         "Réunion (La Réunion)",
47155         "re",
47156         "262",
47157         0
47158       ],
47159       [
47160         "Romania (România)",
47161         "ro",
47162         "40"
47163       ],
47164       [
47165         "Russia (Россия)",
47166         "ru",
47167         "7",
47168         0
47169       ],
47170       [
47171         "Rwanda",
47172         "rw",
47173         "250"
47174       ],
47175       [
47176         "Saint Barthélemy",
47177         "bl",
47178         "590",
47179         1
47180       ],
47181       [
47182         "Saint Helena",
47183         "sh",
47184         "290"
47185       ],
47186       [
47187         "Saint Kitts and Nevis",
47188         "kn",
47189         "1869"
47190       ],
47191       [
47192         "Saint Lucia",
47193         "lc",
47194         "1758"
47195       ],
47196       [
47197         "Saint Martin (Saint-Martin (partie française))",
47198         "mf",
47199         "590",
47200         2
47201       ],
47202       [
47203         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47204         "pm",
47205         "508"
47206       ],
47207       [
47208         "Saint Vincent and the Grenadines",
47209         "vc",
47210         "1784"
47211       ],
47212       [
47213         "Samoa",
47214         "ws",
47215         "685"
47216       ],
47217       [
47218         "San Marino",
47219         "sm",
47220         "378"
47221       ],
47222       [
47223         "São Tomé and Príncipe (São Tomé e Príncipe)",
47224         "st",
47225         "239"
47226       ],
47227       [
47228         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47229         "sa",
47230         "966"
47231       ],
47232       [
47233         "Senegal (Sénégal)",
47234         "sn",
47235         "221"
47236       ],
47237       [
47238         "Serbia (Србија)",
47239         "rs",
47240         "381"
47241       ],
47242       [
47243         "Seychelles",
47244         "sc",
47245         "248"
47246       ],
47247       [
47248         "Sierra Leone",
47249         "sl",
47250         "232"
47251       ],
47252       [
47253         "Singapore",
47254         "sg",
47255         "65"
47256       ],
47257       [
47258         "Sint Maarten",
47259         "sx",
47260         "1721"
47261       ],
47262       [
47263         "Slovakia (Slovensko)",
47264         "sk",
47265         "421"
47266       ],
47267       [
47268         "Slovenia (Slovenija)",
47269         "si",
47270         "386"
47271       ],
47272       [
47273         "Solomon Islands",
47274         "sb",
47275         "677"
47276       ],
47277       [
47278         "Somalia (Soomaaliya)",
47279         "so",
47280         "252"
47281       ],
47282       [
47283         "South Africa",
47284         "za",
47285         "27"
47286       ],
47287       [
47288         "South Korea (대한민국)",
47289         "kr",
47290         "82"
47291       ],
47292       [
47293         "South Sudan (‫جنوب السودان‬‎)",
47294         "ss",
47295         "211"
47296       ],
47297       [
47298         "Spain (España)",
47299         "es",
47300         "34"
47301       ],
47302       [
47303         "Sri Lanka (ශ්‍රී ලංකාව)",
47304         "lk",
47305         "94"
47306       ],
47307       [
47308         "Sudan (‫السودان‬‎)",
47309         "sd",
47310         "249"
47311       ],
47312       [
47313         "Suriname",
47314         "sr",
47315         "597"
47316       ],
47317       [
47318         "Svalbard and Jan Mayen",
47319         "sj",
47320         "47",
47321         1
47322       ],
47323       [
47324         "Swaziland",
47325         "sz",
47326         "268"
47327       ],
47328       [
47329         "Sweden (Sverige)",
47330         "se",
47331         "46"
47332       ],
47333       [
47334         "Switzerland (Schweiz)",
47335         "ch",
47336         "41"
47337       ],
47338       [
47339         "Syria (‫سوريا‬‎)",
47340         "sy",
47341         "963"
47342       ],
47343       [
47344         "Taiwan (台灣)",
47345         "tw",
47346         "886"
47347       ],
47348       [
47349         "Tajikistan",
47350         "tj",
47351         "992"
47352       ],
47353       [
47354         "Tanzania",
47355         "tz",
47356         "255"
47357       ],
47358       [
47359         "Thailand (ไทย)",
47360         "th",
47361         "66"
47362       ],
47363       [
47364         "Timor-Leste",
47365         "tl",
47366         "670"
47367       ],
47368       [
47369         "Togo",
47370         "tg",
47371         "228"
47372       ],
47373       [
47374         "Tokelau",
47375         "tk",
47376         "690"
47377       ],
47378       [
47379         "Tonga",
47380         "to",
47381         "676"
47382       ],
47383       [
47384         "Trinidad and Tobago",
47385         "tt",
47386         "1868"
47387       ],
47388       [
47389         "Tunisia (‫تونس‬‎)",
47390         "tn",
47391         "216"
47392       ],
47393       [
47394         "Turkey (Türkiye)",
47395         "tr",
47396         "90"
47397       ],
47398       [
47399         "Turkmenistan",
47400         "tm",
47401         "993"
47402       ],
47403       [
47404         "Turks and Caicos Islands",
47405         "tc",
47406         "1649"
47407       ],
47408       [
47409         "Tuvalu",
47410         "tv",
47411         "688"
47412       ],
47413       [
47414         "U.S. Virgin Islands",
47415         "vi",
47416         "1340"
47417       ],
47418       [
47419         "Uganda",
47420         "ug",
47421         "256"
47422       ],
47423       [
47424         "Ukraine (Україна)",
47425         "ua",
47426         "380"
47427       ],
47428       [
47429         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47430         "ae",
47431         "971"
47432       ],
47433       [
47434         "United Kingdom",
47435         "gb",
47436         "44",
47437         0
47438       ],
47439       [
47440         "United States",
47441         "us",
47442         "1",
47443         0
47444       ],
47445       [
47446         "Uruguay",
47447         "uy",
47448         "598"
47449       ],
47450       [
47451         "Uzbekistan (Oʻzbekiston)",
47452         "uz",
47453         "998"
47454       ],
47455       [
47456         "Vanuatu",
47457         "vu",
47458         "678"
47459       ],
47460       [
47461         "Vatican City (Città del Vaticano)",
47462         "va",
47463         "39",
47464         1
47465       ],
47466       [
47467         "Venezuela",
47468         "ve",
47469         "58"
47470       ],
47471       [
47472         "Vietnam (Việt Nam)",
47473         "vn",
47474         "84"
47475       ],
47476       [
47477         "Wallis and Futuna (Wallis-et-Futuna)",
47478         "wf",
47479         "681"
47480       ],
47481       [
47482         "Western Sahara (‫الصحراء الغربية‬‎)",
47483         "eh",
47484         "212",
47485         1
47486       ],
47487       [
47488         "Yemen (‫اليمن‬‎)",
47489         "ye",
47490         "967"
47491       ],
47492       [
47493         "Zambia",
47494         "zm",
47495         "260"
47496       ],
47497       [
47498         "Zimbabwe",
47499         "zw",
47500         "263"
47501       ],
47502       [
47503         "Åland Islands",
47504         "ax",
47505         "358",
47506         1
47507       ]
47508   ];
47509   
47510   return d;
47511 }/**
47512 *    This script refer to:
47513 *    Title: International Telephone Input
47514 *    Author: Jack O'Connor
47515 *    Code version:  v12.1.12
47516 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47517 **/
47518
47519 /**
47520  * @class Roo.bootstrap.form.PhoneInput
47521  * @extends Roo.bootstrap.form.TriggerField
47522  * An input with International dial-code selection
47523  
47524  * @cfg {String} defaultDialCode default '+852'
47525  * @cfg {Array} preferedCountries default []
47526   
47527  * @constructor
47528  * Create a new PhoneInput.
47529  * @param {Object} config Configuration options
47530  */
47531
47532 Roo.bootstrap.form.PhoneInput = function(config) {
47533     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47534 };
47535
47536 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47537         /**
47538         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47539         */
47540         listWidth: undefined,
47541         
47542         selectedClass: 'active',
47543         
47544         invalidClass : "has-warning",
47545         
47546         validClass: 'has-success',
47547         
47548         allowed: '0123456789',
47549         
47550         max_length: 15,
47551         
47552         /**
47553          * @cfg {String} defaultDialCode The default dial code when initializing the input
47554          */
47555         defaultDialCode: '+852',
47556         
47557         /**
47558          * @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
47559          */
47560         preferedCountries: false,
47561         
47562         getAutoCreate : function()
47563         {
47564             var data = Roo.bootstrap.form.PhoneInputData();
47565             var align = this.labelAlign || this.parentLabelAlign();
47566             var id = Roo.id();
47567             
47568             this.allCountries = [];
47569             this.dialCodeMapping = [];
47570             
47571             for (var i = 0; i < data.length; i++) {
47572               var c = data[i];
47573               this.allCountries[i] = {
47574                 name: c[0],
47575                 iso2: c[1],
47576                 dialCode: c[2],
47577                 priority: c[3] || 0,
47578                 areaCodes: c[4] || null
47579               };
47580               this.dialCodeMapping[c[2]] = {
47581                   name: c[0],
47582                   iso2: c[1],
47583                   priority: c[3] || 0,
47584                   areaCodes: c[4] || null
47585               };
47586             }
47587             
47588             var cfg = {
47589                 cls: 'form-group',
47590                 cn: []
47591             };
47592             
47593             var input =  {
47594                 tag: 'input',
47595                 id : id,
47596                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
47597                 maxlength: this.max_length,
47598                 cls : 'form-control tel-input',
47599                 autocomplete: 'new-password'
47600             };
47601             
47602             var hiddenInput = {
47603                 tag: 'input',
47604                 type: 'hidden',
47605                 cls: 'hidden-tel-input'
47606             };
47607             
47608             if (this.name) {
47609                 hiddenInput.name = this.name;
47610             }
47611             
47612             if (this.disabled) {
47613                 input.disabled = true;
47614             }
47615             
47616             var flag_container = {
47617                 tag: 'div',
47618                 cls: 'flag-box',
47619                 cn: [
47620                     {
47621                         tag: 'div',
47622                         cls: 'flag'
47623                     },
47624                     {
47625                         tag: 'div',
47626                         cls: 'caret'
47627                     }
47628                 ]
47629             };
47630             
47631             var box = {
47632                 tag: 'div',
47633                 cls: this.hasFeedback ? 'has-feedback' : '',
47634                 cn: [
47635                     hiddenInput,
47636                     input,
47637                     {
47638                         tag: 'input',
47639                         cls: 'dial-code-holder',
47640                         disabled: true
47641                     }
47642                 ]
47643             };
47644             
47645             var container = {
47646                 cls: 'roo-select2-container input-group',
47647                 cn: [
47648                     flag_container,
47649                     box
47650                 ]
47651             };
47652             
47653             if (this.fieldLabel.length) {
47654                 var indicator = {
47655                     tag: 'i',
47656                     tooltip: 'This field is required'
47657                 };
47658                 
47659                 var label = {
47660                     tag: 'label',
47661                     'for':  id,
47662                     cls: 'control-label',
47663                     cn: []
47664                 };
47665                 
47666                 var label_text = {
47667                     tag: 'span',
47668                     html: this.fieldLabel
47669                 };
47670                 
47671                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
47672                 label.cn = [
47673                     indicator,
47674                     label_text
47675                 ];
47676                 
47677                 if(this.indicatorpos == 'right') {
47678                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
47679                     label.cn = [
47680                         label_text,
47681                         indicator
47682                     ];
47683                 }
47684                 
47685                 if(align == 'left') {
47686                     container = {
47687                         tag: 'div',
47688                         cn: [
47689                             container
47690                         ]
47691                     };
47692                     
47693                     if(this.labelWidth > 12){
47694                         label.style = "width: " + this.labelWidth + 'px';
47695                     }
47696                     if(this.labelWidth < 13 && this.labelmd == 0){
47697                         this.labelmd = this.labelWidth;
47698                     }
47699                     if(this.labellg > 0){
47700                         label.cls += ' col-lg-' + this.labellg;
47701                         input.cls += ' col-lg-' + (12 - this.labellg);
47702                     }
47703                     if(this.labelmd > 0){
47704                         label.cls += ' col-md-' + this.labelmd;
47705                         container.cls += ' col-md-' + (12 - this.labelmd);
47706                     }
47707                     if(this.labelsm > 0){
47708                         label.cls += ' col-sm-' + this.labelsm;
47709                         container.cls += ' col-sm-' + (12 - this.labelsm);
47710                     }
47711                     if(this.labelxs > 0){
47712                         label.cls += ' col-xs-' + this.labelxs;
47713                         container.cls += ' col-xs-' + (12 - this.labelxs);
47714                     }
47715                 }
47716             }
47717             
47718             cfg.cn = [
47719                 label,
47720                 container
47721             ];
47722             
47723             var settings = this;
47724             
47725             ['xs','sm','md','lg'].map(function(size){
47726                 if (settings[size]) {
47727                     cfg.cls += ' col-' + size + '-' + settings[size];
47728                 }
47729             });
47730             
47731             this.store = new Roo.data.Store({
47732                 proxy : new Roo.data.MemoryProxy({}),
47733                 reader : new Roo.data.JsonReader({
47734                     fields : [
47735                         {
47736                             'name' : 'name',
47737                             'type' : 'string'
47738                         },
47739                         {
47740                             'name' : 'iso2',
47741                             'type' : 'string'
47742                         },
47743                         {
47744                             'name' : 'dialCode',
47745                             'type' : 'string'
47746                         },
47747                         {
47748                             'name' : 'priority',
47749                             'type' : 'string'
47750                         },
47751                         {
47752                             'name' : 'areaCodes',
47753                             'type' : 'string'
47754                         }
47755                     ]
47756                 })
47757             });
47758             
47759             if(!this.preferedCountries) {
47760                 this.preferedCountries = [
47761                     'hk',
47762                     'gb',
47763                     'us'
47764                 ];
47765             }
47766             
47767             var p = this.preferedCountries.reverse();
47768             
47769             if(p) {
47770                 for (var i = 0; i < p.length; i++) {
47771                     for (var j = 0; j < this.allCountries.length; j++) {
47772                         if(this.allCountries[j].iso2 == p[i]) {
47773                             var t = this.allCountries[j];
47774                             this.allCountries.splice(j,1);
47775                             this.allCountries.unshift(t);
47776                         }
47777                     } 
47778                 }
47779             }
47780             
47781             this.store.proxy.data = {
47782                 success: true,
47783                 data: this.allCountries
47784             };
47785             
47786             return cfg;
47787         },
47788         
47789         initEvents : function()
47790         {
47791             this.createList();
47792             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
47793             
47794             this.indicator = this.indicatorEl();
47795             this.flag = this.flagEl();
47796             this.dialCodeHolder = this.dialCodeHolderEl();
47797             
47798             this.trigger = this.el.select('div.flag-box',true).first();
47799             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
47800             
47801             var _this = this;
47802             
47803             (function(){
47804                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47805                 _this.list.setWidth(lw);
47806             }).defer(100);
47807             
47808             this.list.on('mouseover', this.onViewOver, this);
47809             this.list.on('mousemove', this.onViewMove, this);
47810             this.inputEl().on("keyup", this.onKeyUp, this);
47811             this.inputEl().on("keypress", this.onKeyPress, this);
47812             
47813             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
47814
47815             this.view = new Roo.View(this.list, this.tpl, {
47816                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47817             });
47818             
47819             this.view.on('click', this.onViewClick, this);
47820             this.setValue(this.defaultDialCode);
47821         },
47822         
47823         onTriggerClick : function(e)
47824         {
47825             Roo.log('trigger click');
47826             if(this.disabled){
47827                 return;
47828             }
47829             
47830             if(this.isExpanded()){
47831                 this.collapse();
47832                 this.hasFocus = false;
47833             }else {
47834                 this.store.load({});
47835                 this.hasFocus = true;
47836                 this.expand();
47837             }
47838         },
47839         
47840         isExpanded : function()
47841         {
47842             return this.list.isVisible();
47843         },
47844         
47845         collapse : function()
47846         {
47847             if(!this.isExpanded()){
47848                 return;
47849             }
47850             this.list.hide();
47851             Roo.get(document).un('mousedown', this.collapseIf, this);
47852             Roo.get(document).un('mousewheel', this.collapseIf, this);
47853             this.fireEvent('collapse', this);
47854             this.validate();
47855         },
47856         
47857         expand : function()
47858         {
47859             Roo.log('expand');
47860
47861             if(this.isExpanded() || !this.hasFocus){
47862                 return;
47863             }
47864             
47865             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
47866             this.list.setWidth(lw);
47867             
47868             this.list.show();
47869             this.restrictHeight();
47870             
47871             Roo.get(document).on('mousedown', this.collapseIf, this);
47872             Roo.get(document).on('mousewheel', this.collapseIf, this);
47873             
47874             this.fireEvent('expand', this);
47875         },
47876         
47877         restrictHeight : function()
47878         {
47879             this.list.alignTo(this.inputEl(), this.listAlign);
47880             this.list.alignTo(this.inputEl(), this.listAlign);
47881         },
47882         
47883         onViewOver : function(e, t)
47884         {
47885             if(this.inKeyMode){
47886                 return;
47887             }
47888             var item = this.view.findItemFromChild(t);
47889             
47890             if(item){
47891                 var index = this.view.indexOf(item);
47892                 this.select(index, false);
47893             }
47894         },
47895
47896         // private
47897         onViewClick : function(view, doFocus, el, e)
47898         {
47899             var index = this.view.getSelectedIndexes()[0];
47900             
47901             var r = this.store.getAt(index);
47902             
47903             if(r){
47904                 this.onSelect(r, index);
47905             }
47906             if(doFocus !== false && !this.blockFocus){
47907                 this.inputEl().focus();
47908             }
47909         },
47910         
47911         onViewMove : function(e, t)
47912         {
47913             this.inKeyMode = false;
47914         },
47915         
47916         select : function(index, scrollIntoView)
47917         {
47918             this.selectedIndex = index;
47919             this.view.select(index);
47920             if(scrollIntoView !== false){
47921                 var el = this.view.getNode(index);
47922                 if(el){
47923                     this.list.scrollChildIntoView(el, false);
47924                 }
47925             }
47926         },
47927         
47928         createList : function()
47929         {
47930             this.list = Roo.get(document.body).createChild({
47931                 tag: 'ul',
47932                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
47933                 style: 'display:none'
47934             });
47935             
47936             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
47937         },
47938         
47939         collapseIf : function(e)
47940         {
47941             var in_combo  = e.within(this.el);
47942             var in_list =  e.within(this.list);
47943             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
47944             
47945             if (in_combo || in_list || is_list) {
47946                 return;
47947             }
47948             this.collapse();
47949         },
47950         
47951         onSelect : function(record, index)
47952         {
47953             if(this.fireEvent('beforeselect', this, record, index) !== false){
47954                 
47955                 this.setFlagClass(record.data.iso2);
47956                 this.setDialCode(record.data.dialCode);
47957                 this.hasFocus = false;
47958                 this.collapse();
47959                 this.fireEvent('select', this, record, index);
47960             }
47961         },
47962         
47963         flagEl : function()
47964         {
47965             var flag = this.el.select('div.flag',true).first();
47966             if(!flag){
47967                 return false;
47968             }
47969             return flag;
47970         },
47971         
47972         dialCodeHolderEl : function()
47973         {
47974             var d = this.el.select('input.dial-code-holder',true).first();
47975             if(!d){
47976                 return false;
47977             }
47978             return d;
47979         },
47980         
47981         setDialCode : function(v)
47982         {
47983             this.dialCodeHolder.dom.value = '+'+v;
47984         },
47985         
47986         setFlagClass : function(n)
47987         {
47988             this.flag.dom.className = 'flag '+n;
47989         },
47990         
47991         getValue : function()
47992         {
47993             var v = this.inputEl().getValue();
47994             if(this.dialCodeHolder) {
47995                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
47996             }
47997             return v;
47998         },
47999         
48000         setValue : function(v)
48001         {
48002             var d = this.getDialCode(v);
48003             
48004             //invalid dial code
48005             if(v.length == 0 || !d || d.length == 0) {
48006                 if(this.rendered){
48007                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48008                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48009                 }
48010                 return;
48011             }
48012             
48013             //valid dial code
48014             this.setFlagClass(this.dialCodeMapping[d].iso2);
48015             this.setDialCode(d);
48016             this.inputEl().dom.value = v.replace('+'+d,'');
48017             this.hiddenEl().dom.value = this.getValue();
48018             
48019             this.validate();
48020         },
48021         
48022         getDialCode : function(v)
48023         {
48024             v = v ||  '';
48025             
48026             if (v.length == 0) {
48027                 return this.dialCodeHolder.dom.value;
48028             }
48029             
48030             var dialCode = "";
48031             if (v.charAt(0) != "+") {
48032                 return false;
48033             }
48034             var numericChars = "";
48035             for (var i = 1; i < v.length; i++) {
48036               var c = v.charAt(i);
48037               if (!isNaN(c)) {
48038                 numericChars += c;
48039                 if (this.dialCodeMapping[numericChars]) {
48040                   dialCode = v.substr(1, i);
48041                 }
48042                 if (numericChars.length == 4) {
48043                   break;
48044                 }
48045               }
48046             }
48047             return dialCode;
48048         },
48049         
48050         reset : function()
48051         {
48052             this.setValue(this.defaultDialCode);
48053             this.validate();
48054         },
48055         
48056         hiddenEl : function()
48057         {
48058             return this.el.select('input.hidden-tel-input',true).first();
48059         },
48060         
48061         // after setting val
48062         onKeyUp : function(e){
48063             this.setValue(this.getValue());
48064         },
48065         
48066         onKeyPress : function(e){
48067             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48068                 e.stopEvent();
48069             }
48070         }
48071         
48072 });
48073 /**
48074  * @class Roo.bootstrap.form.MoneyField
48075  * @extends Roo.bootstrap.form.ComboBox
48076  * Bootstrap MoneyField class
48077  * 
48078  * @constructor
48079  * Create a new MoneyField.
48080  * @param {Object} config Configuration options
48081  */
48082
48083 Roo.bootstrap.form.MoneyField = function(config) {
48084     
48085     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48086     
48087 };
48088
48089 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48090     
48091     /**
48092      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48093      */
48094     allowDecimals : true,
48095     /**
48096      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48097      */
48098     decimalSeparator : ".",
48099     /**
48100      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48101      */
48102     decimalPrecision : 0,
48103     /**
48104      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48105      */
48106     allowNegative : true,
48107     /**
48108      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48109      */
48110     allowZero: true,
48111     /**
48112      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48113      */
48114     minValue : Number.NEGATIVE_INFINITY,
48115     /**
48116      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48117      */
48118     maxValue : Number.MAX_VALUE,
48119     /**
48120      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48121      */
48122     minText : "The minimum value for this field is {0}",
48123     /**
48124      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48125      */
48126     maxText : "The maximum value for this field is {0}",
48127     /**
48128      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48129      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48130      */
48131     nanText : "{0} is not a valid number",
48132     /**
48133      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48134      */
48135     castInt : true,
48136     /**
48137      * @cfg {String} defaults currency of the MoneyField
48138      * value should be in lkey
48139      */
48140     defaultCurrency : false,
48141     /**
48142      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48143      */
48144     thousandsDelimiter : false,
48145     /**
48146      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48147      */
48148     max_length: false,
48149     
48150     inputlg : 9,
48151     inputmd : 9,
48152     inputsm : 9,
48153     inputxs : 6,
48154      /**
48155      * @cfg {Roo.data.Store} store  Store to lookup currency??
48156      */
48157     store : false,
48158     
48159     getAutoCreate : function()
48160     {
48161         var align = this.labelAlign || this.parentLabelAlign();
48162         
48163         var id = Roo.id();
48164
48165         var cfg = {
48166             cls: 'form-group',
48167             cn: []
48168         };
48169
48170         var input =  {
48171             tag: 'input',
48172             id : id,
48173             cls : 'form-control roo-money-amount-input',
48174             autocomplete: 'new-password'
48175         };
48176         
48177         var hiddenInput = {
48178             tag: 'input',
48179             type: 'hidden',
48180             id: Roo.id(),
48181             cls: 'hidden-number-input'
48182         };
48183         
48184         if(this.max_length) {
48185             input.maxlength = this.max_length; 
48186         }
48187         
48188         if (this.name) {
48189             hiddenInput.name = this.name;
48190         }
48191
48192         if (this.disabled) {
48193             input.disabled = true;
48194         }
48195
48196         var clg = 12 - this.inputlg;
48197         var cmd = 12 - this.inputmd;
48198         var csm = 12 - this.inputsm;
48199         var cxs = 12 - this.inputxs;
48200         
48201         var container = {
48202             tag : 'div',
48203             cls : 'row roo-money-field',
48204             cn : [
48205                 {
48206                     tag : 'div',
48207                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48208                     cn : [
48209                         {
48210                             tag : 'div',
48211                             cls: 'roo-select2-container input-group',
48212                             cn: [
48213                                 {
48214                                     tag : 'input',
48215                                     cls : 'form-control roo-money-currency-input',
48216                                     autocomplete: 'new-password',
48217                                     readOnly : 1,
48218                                     name : this.currencyName
48219                                 },
48220                                 {
48221                                     tag :'span',
48222                                     cls : 'input-group-addon',
48223                                     cn : [
48224                                         {
48225                                             tag: 'span',
48226                                             cls: 'caret'
48227                                         }
48228                                     ]
48229                                 }
48230                             ]
48231                         }
48232                     ]
48233                 },
48234                 {
48235                     tag : 'div',
48236                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48237                     cn : [
48238                         {
48239                             tag: 'div',
48240                             cls: this.hasFeedback ? 'has-feedback' : '',
48241                             cn: [
48242                                 input
48243                             ]
48244                         }
48245                     ]
48246                 }
48247             ]
48248             
48249         };
48250         
48251         if (this.fieldLabel.length) {
48252             var indicator = {
48253                 tag: 'i',
48254                 tooltip: 'This field is required'
48255             };
48256
48257             var label = {
48258                 tag: 'label',
48259                 'for':  id,
48260                 cls: 'control-label',
48261                 cn: []
48262             };
48263
48264             var label_text = {
48265                 tag: 'span',
48266                 html: this.fieldLabel
48267             };
48268
48269             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48270             label.cn = [
48271                 indicator,
48272                 label_text
48273             ];
48274
48275             if(this.indicatorpos == 'right') {
48276                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48277                 label.cn = [
48278                     label_text,
48279                     indicator
48280                 ];
48281             }
48282
48283             if(align == 'left') {
48284                 container = {
48285                     tag: 'div',
48286                     cn: [
48287                         container
48288                     ]
48289                 };
48290
48291                 if(this.labelWidth > 12){
48292                     label.style = "width: " + this.labelWidth + 'px';
48293                 }
48294                 if(this.labelWidth < 13 && this.labelmd == 0){
48295                     this.labelmd = this.labelWidth;
48296                 }
48297                 if(this.labellg > 0){
48298                     label.cls += ' col-lg-' + this.labellg;
48299                     input.cls += ' col-lg-' + (12 - this.labellg);
48300                 }
48301                 if(this.labelmd > 0){
48302                     label.cls += ' col-md-' + this.labelmd;
48303                     container.cls += ' col-md-' + (12 - this.labelmd);
48304                 }
48305                 if(this.labelsm > 0){
48306                     label.cls += ' col-sm-' + this.labelsm;
48307                     container.cls += ' col-sm-' + (12 - this.labelsm);
48308                 }
48309                 if(this.labelxs > 0){
48310                     label.cls += ' col-xs-' + this.labelxs;
48311                     container.cls += ' col-xs-' + (12 - this.labelxs);
48312                 }
48313             }
48314         }
48315
48316         cfg.cn = [
48317             label,
48318             container,
48319             hiddenInput
48320         ];
48321         
48322         var settings = this;
48323
48324         ['xs','sm','md','lg'].map(function(size){
48325             if (settings[size]) {
48326                 cfg.cls += ' col-' + size + '-' + settings[size];
48327             }
48328         });
48329         
48330         return cfg;
48331     },
48332     
48333     initEvents : function()
48334     {
48335         this.indicator = this.indicatorEl();
48336         
48337         this.initCurrencyEvent();
48338         
48339         this.initNumberEvent();
48340     },
48341     
48342     initCurrencyEvent : function()
48343     {
48344         if (!this.store) {
48345             throw "can not find store for combo";
48346         }
48347         
48348         this.store = Roo.factory(this.store, Roo.data);
48349         this.store.parent = this;
48350         
48351         this.createList();
48352         
48353         this.triggerEl = this.el.select('.input-group-addon', true).first();
48354         
48355         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48356         
48357         var _this = this;
48358         
48359         (function(){
48360             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48361             _this.list.setWidth(lw);
48362         }).defer(100);
48363         
48364         this.list.on('mouseover', this.onViewOver, this);
48365         this.list.on('mousemove', this.onViewMove, this);
48366         this.list.on('scroll', this.onViewScroll, this);
48367         
48368         if(!this.tpl){
48369             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48370         }
48371         
48372         this.view = new Roo.View(this.list, this.tpl, {
48373             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48374         });
48375         
48376         this.view.on('click', this.onViewClick, this);
48377         
48378         this.store.on('beforeload', this.onBeforeLoad, this);
48379         this.store.on('load', this.onLoad, this);
48380         this.store.on('loadexception', this.onLoadException, this);
48381         
48382         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48383             "up" : function(e){
48384                 this.inKeyMode = true;
48385                 this.selectPrev();
48386             },
48387
48388             "down" : function(e){
48389                 if(!this.isExpanded()){
48390                     this.onTriggerClick();
48391                 }else{
48392                     this.inKeyMode = true;
48393                     this.selectNext();
48394                 }
48395             },
48396
48397             "enter" : function(e){
48398                 this.collapse();
48399                 
48400                 if(this.fireEvent("specialkey", this, e)){
48401                     this.onViewClick(false);
48402                 }
48403                 
48404                 return true;
48405             },
48406
48407             "esc" : function(e){
48408                 this.collapse();
48409             },
48410
48411             "tab" : function(e){
48412                 this.collapse();
48413                 
48414                 if(this.fireEvent("specialkey", this, e)){
48415                     this.onViewClick(false);
48416                 }
48417                 
48418                 return true;
48419             },
48420
48421             scope : this,
48422
48423             doRelay : function(foo, bar, hname){
48424                 if(hname == 'down' || this.scope.isExpanded()){
48425                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48426                 }
48427                 return true;
48428             },
48429
48430             forceKeyDown: true
48431         });
48432         
48433         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48434         
48435     },
48436     
48437     initNumberEvent : function(e)
48438     {
48439         this.inputEl().on("keydown" , this.fireKey,  this);
48440         this.inputEl().on("focus", this.onFocus,  this);
48441         this.inputEl().on("blur", this.onBlur,  this);
48442         
48443         this.inputEl().relayEvent('keyup', this);
48444         
48445         if(this.indicator){
48446             this.indicator.addClass('invisible');
48447         }
48448  
48449         this.originalValue = this.getValue();
48450         
48451         if(this.validationEvent == 'keyup'){
48452             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48453             this.inputEl().on('keyup', this.filterValidation, this);
48454         }
48455         else if(this.validationEvent !== false){
48456             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48457         }
48458         
48459         if(this.selectOnFocus){
48460             this.on("focus", this.preFocus, this);
48461             
48462         }
48463         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48464             this.inputEl().on("keypress", this.filterKeys, this);
48465         } else {
48466             this.inputEl().relayEvent('keypress', this);
48467         }
48468         
48469         var allowed = "0123456789";
48470         
48471         if(this.allowDecimals){
48472             allowed += this.decimalSeparator;
48473         }
48474         
48475         if(this.allowNegative){
48476             allowed += "-";
48477         }
48478         
48479         if(this.thousandsDelimiter) {
48480             allowed += ",";
48481         }
48482         
48483         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48484         
48485         var keyPress = function(e){
48486             
48487             var k = e.getKey();
48488             
48489             var c = e.getCharCode();
48490             
48491             if(
48492                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48493                     allowed.indexOf(String.fromCharCode(c)) === -1
48494             ){
48495                 e.stopEvent();
48496                 return;
48497             }
48498             
48499             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48500                 return;
48501             }
48502             
48503             if(allowed.indexOf(String.fromCharCode(c)) === -1){
48504                 e.stopEvent();
48505             }
48506         };
48507         
48508         this.inputEl().on("keypress", keyPress, this);
48509         
48510     },
48511     
48512     onTriggerClick : function(e)
48513     {   
48514         if(this.disabled){
48515             return;
48516         }
48517         
48518         this.page = 0;
48519         this.loadNext = false;
48520         
48521         if(this.isExpanded()){
48522             this.collapse();
48523             return;
48524         }
48525         
48526         this.hasFocus = true;
48527         
48528         if(this.triggerAction == 'all') {
48529             this.doQuery(this.allQuery, true);
48530             return;
48531         }
48532         
48533         this.doQuery(this.getRawValue());
48534     },
48535     
48536     getCurrency : function()
48537     {   
48538         var v = this.currencyEl().getValue();
48539         
48540         return v;
48541     },
48542     
48543     restrictHeight : function()
48544     {
48545         this.list.alignTo(this.currencyEl(), this.listAlign);
48546         this.list.alignTo(this.currencyEl(), this.listAlign);
48547     },
48548     
48549     onViewClick : function(view, doFocus, el, e)
48550     {
48551         var index = this.view.getSelectedIndexes()[0];
48552         
48553         var r = this.store.getAt(index);
48554         
48555         if(r){
48556             this.onSelect(r, index);
48557         }
48558     },
48559     
48560     onSelect : function(record, index){
48561         
48562         if(this.fireEvent('beforeselect', this, record, index) !== false){
48563         
48564             this.setFromCurrencyData(index > -1 ? record.data : false);
48565             
48566             this.collapse();
48567             
48568             this.fireEvent('select', this, record, index);
48569         }
48570     },
48571     
48572     setFromCurrencyData : function(o)
48573     {
48574         var currency = '';
48575         
48576         this.lastCurrency = o;
48577         
48578         if (this.currencyField) {
48579             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
48580         } else {
48581             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
48582         }
48583         
48584         this.lastSelectionText = currency;
48585         
48586         //setting default currency
48587         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
48588             this.setCurrency(this.defaultCurrency);
48589             return;
48590         }
48591         
48592         this.setCurrency(currency);
48593     },
48594     
48595     setFromData : function(o)
48596     {
48597         var c = {};
48598         
48599         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
48600         
48601         this.setFromCurrencyData(c);
48602         
48603         var value = '';
48604         
48605         if (this.name) {
48606             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
48607         } else {
48608             Roo.log('no value set for '+ (this.name ? this.name : this.id));
48609         }
48610         
48611         this.setValue(value);
48612         
48613     },
48614     
48615     setCurrency : function(v)
48616     {   
48617         this.currencyValue = v;
48618         
48619         if(this.rendered){
48620             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
48621             this.validate();
48622         }
48623     },
48624     
48625     setValue : function(v)
48626     {
48627         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
48628         
48629         this.value = v;
48630         
48631         if(this.rendered){
48632             
48633             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48634             
48635             this.inputEl().dom.value = (v == '') ? '' :
48636                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
48637             
48638             if(!this.allowZero && v === '0') {
48639                 this.hiddenEl().dom.value = '';
48640                 this.inputEl().dom.value = '';
48641             }
48642             
48643             this.validate();
48644         }
48645     },
48646     
48647     getRawValue : function()
48648     {
48649         var v = this.inputEl().getValue();
48650         
48651         return v;
48652     },
48653     
48654     getValue : function()
48655     {
48656         return this.fixPrecision(this.parseValue(this.getRawValue()));
48657     },
48658     
48659     parseValue : function(value)
48660     {
48661         if(this.thousandsDelimiter) {
48662             value += "";
48663             r = new RegExp(",", "g");
48664             value = value.replace(r, "");
48665         }
48666         
48667         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
48668         return isNaN(value) ? '' : value;
48669         
48670     },
48671     
48672     fixPrecision : function(value)
48673     {
48674         if(this.thousandsDelimiter) {
48675             value += "";
48676             r = new RegExp(",", "g");
48677             value = value.replace(r, "");
48678         }
48679         
48680         var nan = isNaN(value);
48681         
48682         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
48683             return nan ? '' : value;
48684         }
48685         return parseFloat(value).toFixed(this.decimalPrecision);
48686     },
48687     
48688     decimalPrecisionFcn : function(v)
48689     {
48690         return Math.floor(v);
48691     },
48692     
48693     validateValue : function(value)
48694     {
48695         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
48696             return false;
48697         }
48698         
48699         var num = this.parseValue(value);
48700         
48701         if(isNaN(num)){
48702             this.markInvalid(String.format(this.nanText, value));
48703             return false;
48704         }
48705         
48706         if(num < this.minValue){
48707             this.markInvalid(String.format(this.minText, this.minValue));
48708             return false;
48709         }
48710         
48711         if(num > this.maxValue){
48712             this.markInvalid(String.format(this.maxText, this.maxValue));
48713             return false;
48714         }
48715         
48716         return true;
48717     },
48718     
48719     validate : function()
48720     {
48721         if(this.disabled || this.allowBlank){
48722             this.markValid();
48723             return true;
48724         }
48725         
48726         var currency = this.getCurrency();
48727         
48728         if(this.validateValue(this.getRawValue()) && currency.length){
48729             this.markValid();
48730             return true;
48731         }
48732         
48733         this.markInvalid();
48734         return false;
48735     },
48736     
48737     getName: function()
48738     {
48739         return this.name;
48740     },
48741     
48742     beforeBlur : function()
48743     {
48744         if(!this.castInt){
48745             return;
48746         }
48747         
48748         var v = this.parseValue(this.getRawValue());
48749         
48750         if(v || v == 0){
48751             this.setValue(v);
48752         }
48753     },
48754     
48755     onBlur : function()
48756     {
48757         this.beforeBlur();
48758         
48759         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
48760             //this.el.removeClass(this.focusClass);
48761         }
48762         
48763         this.hasFocus = false;
48764         
48765         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
48766             this.validate();
48767         }
48768         
48769         var v = this.getValue();
48770         
48771         if(String(v) !== String(this.startValue)){
48772             this.fireEvent('change', this, v, this.startValue);
48773         }
48774         
48775         this.fireEvent("blur", this);
48776     },
48777     
48778     inputEl : function()
48779     {
48780         return this.el.select('.roo-money-amount-input', true).first();
48781     },
48782     
48783     currencyEl : function()
48784     {
48785         return this.el.select('.roo-money-currency-input', true).first();
48786     },
48787     
48788     hiddenEl : function()
48789     {
48790         return this.el.select('input.hidden-number-input',true).first();
48791     }
48792     
48793 });/**
48794  * @class Roo.bootstrap.BezierSignature
48795  * @extends Roo.bootstrap.Component
48796  * Bootstrap BezierSignature class
48797  * This script refer to:
48798  *    Title: Signature Pad
48799  *    Author: szimek
48800  *    Availability: https://github.com/szimek/signature_pad
48801  *
48802  * @constructor
48803  * Create a new BezierSignature
48804  * @param {Object} config The config object
48805  */
48806
48807 Roo.bootstrap.BezierSignature = function(config){
48808     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
48809     this.addEvents({
48810         "resize" : true
48811     });
48812 };
48813
48814 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
48815 {
48816      
48817     curve_data: [],
48818     
48819     is_empty: true,
48820     
48821     mouse_btn_down: true,
48822     
48823     /**
48824      * @cfg {int} canvas height
48825      */
48826     canvas_height: '200px',
48827     
48828     /**
48829      * @cfg {float|function} Radius of a single dot.
48830      */ 
48831     dot_size: false,
48832     
48833     /**
48834      * @cfg {float} Minimum width of a line. Defaults to 0.5.
48835      */
48836     min_width: 0.5,
48837     
48838     /**
48839      * @cfg {float} Maximum width of a line. Defaults to 2.5.
48840      */
48841     max_width: 2.5,
48842     
48843     /**
48844      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
48845      */
48846     throttle: 16,
48847     
48848     /**
48849      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
48850      */
48851     min_distance: 5,
48852     
48853     /**
48854      * @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.
48855      */
48856     bg_color: 'rgba(0, 0, 0, 0)',
48857     
48858     /**
48859      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
48860      */
48861     dot_color: 'black',
48862     
48863     /**
48864      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
48865      */ 
48866     velocity_filter_weight: 0.7,
48867     
48868     /**
48869      * @cfg {function} Callback when stroke begin. 
48870      */
48871     onBegin: false,
48872     
48873     /**
48874      * @cfg {function} Callback when stroke end.
48875      */
48876     onEnd: false,
48877     
48878     getAutoCreate : function()
48879     {
48880         var cls = 'roo-signature column';
48881         
48882         if(this.cls){
48883             cls += ' ' + this.cls;
48884         }
48885         
48886         var col_sizes = [
48887             'lg',
48888             'md',
48889             'sm',
48890             'xs'
48891         ];
48892         
48893         for(var i = 0; i < col_sizes.length; i++) {
48894             if(this[col_sizes[i]]) {
48895                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
48896             }
48897         }
48898         
48899         var cfg = {
48900             tag: 'div',
48901             cls: cls,
48902             cn: [
48903                 {
48904                     tag: 'div',
48905                     cls: 'roo-signature-body',
48906                     cn: [
48907                         {
48908                             tag: 'canvas',
48909                             cls: 'roo-signature-body-canvas',
48910                             height: this.canvas_height,
48911                             width: this.canvas_width
48912                         }
48913                     ]
48914                 },
48915                 {
48916                     tag: 'input',
48917                     type: 'file',
48918                     style: 'display: none'
48919                 }
48920             ]
48921         };
48922         
48923         return cfg;
48924     },
48925     
48926     initEvents: function() 
48927     {
48928         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
48929         
48930         var canvas = this.canvasEl();
48931         
48932         // mouse && touch event swapping...
48933         canvas.dom.style.touchAction = 'none';
48934         canvas.dom.style.msTouchAction = 'none';
48935         
48936         this.mouse_btn_down = false;
48937         canvas.on('mousedown', this._handleMouseDown, this);
48938         canvas.on('mousemove', this._handleMouseMove, this);
48939         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
48940         
48941         if (window.PointerEvent) {
48942             canvas.on('pointerdown', this._handleMouseDown, this);
48943             canvas.on('pointermove', this._handleMouseMove, this);
48944             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
48945         }
48946         
48947         if ('ontouchstart' in window) {
48948             canvas.on('touchstart', this._handleTouchStart, this);
48949             canvas.on('touchmove', this._handleTouchMove, this);
48950             canvas.on('touchend', this._handleTouchEnd, this);
48951         }
48952         
48953         Roo.EventManager.onWindowResize(this.resize, this, true);
48954         
48955         // file input event
48956         this.fileEl().on('change', this.uploadImage, this);
48957         
48958         this.clear();
48959         
48960         this.resize();
48961     },
48962     
48963     resize: function(){
48964         
48965         var canvas = this.canvasEl().dom;
48966         var ctx = this.canvasElCtx();
48967         var img_data = false;
48968         
48969         if(canvas.width > 0) {
48970             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
48971         }
48972         // setting canvas width will clean img data
48973         canvas.width = 0;
48974         
48975         var style = window.getComputedStyle ? 
48976             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
48977             
48978         var padding_left = parseInt(style.paddingLeft) || 0;
48979         var padding_right = parseInt(style.paddingRight) || 0;
48980         
48981         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
48982         
48983         if(img_data) {
48984             ctx.putImageData(img_data, 0, 0);
48985         }
48986     },
48987     
48988     _handleMouseDown: function(e)
48989     {
48990         if (e.browserEvent.which === 1) {
48991             this.mouse_btn_down = true;
48992             this.strokeBegin(e);
48993         }
48994     },
48995     
48996     _handleMouseMove: function (e)
48997     {
48998         if (this.mouse_btn_down) {
48999             this.strokeMoveUpdate(e);
49000         }
49001     },
49002     
49003     _handleMouseUp: function (e)
49004     {
49005         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49006             this.mouse_btn_down = false;
49007             this.strokeEnd(e);
49008         }
49009     },
49010     
49011     _handleTouchStart: function (e) {
49012         
49013         e.preventDefault();
49014         if (e.browserEvent.targetTouches.length === 1) {
49015             // var touch = e.browserEvent.changedTouches[0];
49016             // this.strokeBegin(touch);
49017             
49018              this.strokeBegin(e); // assume e catching the correct xy...
49019         }
49020     },
49021     
49022     _handleTouchMove: function (e) {
49023         e.preventDefault();
49024         // var touch = event.targetTouches[0];
49025         // _this._strokeMoveUpdate(touch);
49026         this.strokeMoveUpdate(e);
49027     },
49028     
49029     _handleTouchEnd: function (e) {
49030         var wasCanvasTouched = e.target === this.canvasEl().dom;
49031         if (wasCanvasTouched) {
49032             e.preventDefault();
49033             // var touch = event.changedTouches[0];
49034             // _this._strokeEnd(touch);
49035             this.strokeEnd(e);
49036         }
49037     },
49038     
49039     reset: function () {
49040         this._lastPoints = [];
49041         this._lastVelocity = 0;
49042         this._lastWidth = (this.min_width + this.max_width) / 2;
49043         this.canvasElCtx().fillStyle = this.dot_color;
49044     },
49045     
49046     strokeMoveUpdate: function(e)
49047     {
49048         this.strokeUpdate(e);
49049         
49050         if (this.throttle) {
49051             this.throttleStroke(this.strokeUpdate, this.throttle);
49052         }
49053         else {
49054             this.strokeUpdate(e);
49055         }
49056     },
49057     
49058     strokeBegin: function(e)
49059     {
49060         var newPointGroup = {
49061             color: this.dot_color,
49062             points: []
49063         };
49064         
49065         if (typeof this.onBegin === 'function') {
49066             this.onBegin(e);
49067         }
49068         
49069         this.curve_data.push(newPointGroup);
49070         this.reset();
49071         this.strokeUpdate(e);
49072     },
49073     
49074     strokeUpdate: function(e)
49075     {
49076         var rect = this.canvasEl().dom.getBoundingClientRect();
49077         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49078         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49079         var lastPoints = lastPointGroup.points;
49080         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49081         var isLastPointTooClose = lastPoint
49082             ? point.distanceTo(lastPoint) <= this.min_distance
49083             : false;
49084         var color = lastPointGroup.color;
49085         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49086             var curve = this.addPoint(point);
49087             if (!lastPoint) {
49088                 this.drawDot({color: color, point: point});
49089             }
49090             else if (curve) {
49091                 this.drawCurve({color: color, curve: curve});
49092             }
49093             lastPoints.push({
49094                 time: point.time,
49095                 x: point.x,
49096                 y: point.y
49097             });
49098         }
49099     },
49100     
49101     strokeEnd: function(e)
49102     {
49103         this.strokeUpdate(e);
49104         if (typeof this.onEnd === 'function') {
49105             this.onEnd(e);
49106         }
49107     },
49108     
49109     addPoint:  function (point) {
49110         var _lastPoints = this._lastPoints;
49111         _lastPoints.push(point);
49112         if (_lastPoints.length > 2) {
49113             if (_lastPoints.length === 3) {
49114                 _lastPoints.unshift(_lastPoints[0]);
49115             }
49116             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49117             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49118             _lastPoints.shift();
49119             return curve;
49120         }
49121         return null;
49122     },
49123     
49124     calculateCurveWidths: function (startPoint, endPoint) {
49125         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49126             (1 - this.velocity_filter_weight) * this._lastVelocity;
49127
49128         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49129         var widths = {
49130             end: newWidth,
49131             start: this._lastWidth
49132         };
49133         
49134         this._lastVelocity = velocity;
49135         this._lastWidth = newWidth;
49136         return widths;
49137     },
49138     
49139     drawDot: function (_a) {
49140         var color = _a.color, point = _a.point;
49141         var ctx = this.canvasElCtx();
49142         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49143         ctx.beginPath();
49144         this.drawCurveSegment(point.x, point.y, width);
49145         ctx.closePath();
49146         ctx.fillStyle = color;
49147         ctx.fill();
49148     },
49149     
49150     drawCurve: function (_a) {
49151         var color = _a.color, curve = _a.curve;
49152         var ctx = this.canvasElCtx();
49153         var widthDelta = curve.endWidth - curve.startWidth;
49154         var drawSteps = Math.floor(curve.length()) * 2;
49155         ctx.beginPath();
49156         ctx.fillStyle = color;
49157         for (var i = 0; i < drawSteps; i += 1) {
49158         var t = i / drawSteps;
49159         var tt = t * t;
49160         var ttt = tt * t;
49161         var u = 1 - t;
49162         var uu = u * u;
49163         var uuu = uu * u;
49164         var x = uuu * curve.startPoint.x;
49165         x += 3 * uu * t * curve.control1.x;
49166         x += 3 * u * tt * curve.control2.x;
49167         x += ttt * curve.endPoint.x;
49168         var y = uuu * curve.startPoint.y;
49169         y += 3 * uu * t * curve.control1.y;
49170         y += 3 * u * tt * curve.control2.y;
49171         y += ttt * curve.endPoint.y;
49172         var width = curve.startWidth + ttt * widthDelta;
49173         this.drawCurveSegment(x, y, width);
49174         }
49175         ctx.closePath();
49176         ctx.fill();
49177     },
49178     
49179     drawCurveSegment: function (x, y, width) {
49180         var ctx = this.canvasElCtx();
49181         ctx.moveTo(x, y);
49182         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49183         this.is_empty = false;
49184     },
49185     
49186     clear: function()
49187     {
49188         var ctx = this.canvasElCtx();
49189         var canvas = this.canvasEl().dom;
49190         ctx.fillStyle = this.bg_color;
49191         ctx.clearRect(0, 0, canvas.width, canvas.height);
49192         ctx.fillRect(0, 0, canvas.width, canvas.height);
49193         this.curve_data = [];
49194         this.reset();
49195         this.is_empty = true;
49196     },
49197     
49198     fileEl: function()
49199     {
49200         return  this.el.select('input',true).first();
49201     },
49202     
49203     canvasEl: function()
49204     {
49205         return this.el.select('canvas',true).first();
49206     },
49207     
49208     canvasElCtx: function()
49209     {
49210         return this.el.select('canvas',true).first().dom.getContext('2d');
49211     },
49212     
49213     getImage: function(type)
49214     {
49215         if(this.is_empty) {
49216             return false;
49217         }
49218         
49219         // encryption ?
49220         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49221     },
49222     
49223     drawFromImage: function(img_src)
49224     {
49225         var img = new Image();
49226         
49227         img.onload = function(){
49228             this.canvasElCtx().drawImage(img, 0, 0);
49229         }.bind(this);
49230         
49231         img.src = img_src;
49232         
49233         this.is_empty = false;
49234     },
49235     
49236     selectImage: function()
49237     {
49238         this.fileEl().dom.click();
49239     },
49240     
49241     uploadImage: function(e)
49242     {
49243         var reader = new FileReader();
49244         
49245         reader.onload = function(e){
49246             var img = new Image();
49247             img.onload = function(){
49248                 this.reset();
49249                 this.canvasElCtx().drawImage(img, 0, 0);
49250             }.bind(this);
49251             img.src = e.target.result;
49252         }.bind(this);
49253         
49254         reader.readAsDataURL(e.target.files[0]);
49255     },
49256     
49257     // Bezier Point Constructor
49258     Point: (function () {
49259         function Point(x, y, time) {
49260             this.x = x;
49261             this.y = y;
49262             this.time = time || Date.now();
49263         }
49264         Point.prototype.distanceTo = function (start) {
49265             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49266         };
49267         Point.prototype.equals = function (other) {
49268             return this.x === other.x && this.y === other.y && this.time === other.time;
49269         };
49270         Point.prototype.velocityFrom = function (start) {
49271             return this.time !== start.time
49272             ? this.distanceTo(start) / (this.time - start.time)
49273             : 0;
49274         };
49275         return Point;
49276     }()),
49277     
49278     
49279     // Bezier Constructor
49280     Bezier: (function () {
49281         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49282             this.startPoint = startPoint;
49283             this.control2 = control2;
49284             this.control1 = control1;
49285             this.endPoint = endPoint;
49286             this.startWidth = startWidth;
49287             this.endWidth = endWidth;
49288         }
49289         Bezier.fromPoints = function (points, widths, scope) {
49290             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49291             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49292             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49293         };
49294         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49295             var dx1 = s1.x - s2.x;
49296             var dy1 = s1.y - s2.y;
49297             var dx2 = s2.x - s3.x;
49298             var dy2 = s2.y - s3.y;
49299             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49300             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49301             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49302             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49303             var dxm = m1.x - m2.x;
49304             var dym = m1.y - m2.y;
49305             var k = l2 / (l1 + l2);
49306             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49307             var tx = s2.x - cm.x;
49308             var ty = s2.y - cm.y;
49309             return {
49310                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49311                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49312             };
49313         };
49314         Bezier.prototype.length = function () {
49315             var steps = 10;
49316             var length = 0;
49317             var px;
49318             var py;
49319             for (var i = 0; i <= steps; i += 1) {
49320                 var t = i / steps;
49321                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49322                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49323                 if (i > 0) {
49324                     var xdiff = cx - px;
49325                     var ydiff = cy - py;
49326                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49327                 }
49328                 px = cx;
49329                 py = cy;
49330             }
49331             return length;
49332         };
49333         Bezier.prototype.point = function (t, start, c1, c2, end) {
49334             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49335             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49336             + (3.0 * c2 * (1.0 - t) * t * t)
49337             + (end * t * t * t);
49338         };
49339         return Bezier;
49340     }()),
49341     
49342     throttleStroke: function(fn, wait) {
49343       if (wait === void 0) { wait = 250; }
49344       var previous = 0;
49345       var timeout = null;
49346       var result;
49347       var storedContext;
49348       var storedArgs;
49349       var later = function () {
49350           previous = Date.now();
49351           timeout = null;
49352           result = fn.apply(storedContext, storedArgs);
49353           if (!timeout) {
49354               storedContext = null;
49355               storedArgs = [];
49356           }
49357       };
49358       return function wrapper() {
49359           var args = [];
49360           for (var _i = 0; _i < arguments.length; _i++) {
49361               args[_i] = arguments[_i];
49362           }
49363           var now = Date.now();
49364           var remaining = wait - (now - previous);
49365           storedContext = this;
49366           storedArgs = args;
49367           if (remaining <= 0 || remaining > wait) {
49368               if (timeout) {
49369                   clearTimeout(timeout);
49370                   timeout = null;
49371               }
49372               previous = now;
49373               result = fn.apply(storedContext, storedArgs);
49374               if (!timeout) {
49375                   storedContext = null;
49376                   storedArgs = [];
49377               }
49378           }
49379           else if (!timeout) {
49380               timeout = window.setTimeout(later, remaining);
49381           }
49382           return result;
49383       };
49384   }
49385   
49386 });
49387
49388  
49389
49390  // old names for form elements
49391 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49392 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49393 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49394 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49395 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49396 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49397 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49398 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49399 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49400 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49401 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49402 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49403 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49404 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49405 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49406 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49407 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49408 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49409 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49410 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49411 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49412 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49413 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49414 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49415 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49416 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49417
49418 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49419 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49420
49421 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49422 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49423
49424 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49425 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49426 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49427 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49428