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             // where does this come from?
9426             //cfg.cls+=  ' TableGrid';
9427         }
9428         
9429         return { cn : [ cfg ] };
9430     },
9431     
9432     initEvents : function()
9433     {   
9434         if(!this.store || !this.cm){
9435             return;
9436         }
9437         if (this.selModel) {
9438             this.selModel.initEvents();
9439         }
9440         
9441         
9442         //Roo.log('initEvents with ds!!!!');
9443         
9444         this.bodyEl = this.el.select('tbody', true).first();
9445         this.headEl = this.el.select('thead', true).first();
9446         this.mainFoot = this.el.select('tfoot', true).first();
9447         
9448         
9449         
9450         
9451         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9452             e.on('click', this.sort, this);
9453         }, this);
9454         
9455         
9456         // why is this done????? = it breaks dialogs??
9457         //this.parent().el.setStyle('position', 'relative');
9458         
9459         
9460         if (this.footer) {
9461             this.footer.parentId = this.id;
9462             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9463             
9464             if(this.lazyLoad){
9465                 this.el.select('tfoot tr td').first().addClass('hide');
9466             }
9467         } 
9468         
9469         if(this.loadMask) {
9470             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9471         }
9472         
9473         this.store.on('load', this.onLoad, this);
9474         this.store.on('beforeload', this.onBeforeLoad, this);
9475         this.store.on('update', this.onUpdate, this);
9476         this.store.on('add', this.onAdd, this);
9477         this.store.on("clear", this.clear, this);
9478         
9479         this.el.on("contextmenu", this.onContextMenu, this);
9480         
9481         
9482         this.cm.on("headerchange", this.onHeaderChange, this);
9483         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9484
9485  //?? does bodyEl get replaced on render?
9486         this.bodyEl.on("click", this.onClick, this);
9487         this.bodyEl.on("dblclick", this.onDblClick, this);        
9488         this.bodyEl.on('scroll', this.onBodyScroll, this);
9489
9490         // guessing mainbody will work - this relays usually caught by selmodel at present.
9491         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9492   
9493   
9494         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9495         
9496   
9497         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9498             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9499         }
9500         
9501         this.initCSS();
9502     },
9503     // Compatibility with grid - we implement all the view features at present.
9504     getView : function()
9505     {
9506         return this;
9507     },
9508     
9509     initCSS : function()
9510     {
9511         if(this.disableAutoSize) {
9512             return;
9513         }
9514         
9515         var cm = this.cm, styles = [];
9516         this.CSS.removeStyleSheet(this.id + '-cssrules');
9517         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9518         // we can honour xs/sm/md/xl  as widths...
9519         // we first have to decide what widht we are currently at...
9520         var sz = Roo.getGridSize();
9521         
9522         var total = 0;
9523         var last = -1;
9524         var cols = []; // visable cols.
9525         var total_abs = 0;
9526         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9527             var w = cm.getColumnWidth(i, false);
9528             if(cm.isHidden(i)){
9529                 cols.push( { rel : false, abs : 0 });
9530                 continue;
9531             }
9532             if (w !== false) {
9533                 cols.push( { rel : false, abs : w });
9534                 total_abs += w;
9535                 last = i; // not really..
9536                 continue;
9537             }
9538             var w = cm.getColumnWidth(i, sz);
9539             if (w > 0) {
9540                 last = i
9541             }
9542             total += w;
9543             cols.push( { rel : w, abs : false });
9544         }
9545         
9546         var avail = this.bodyEl.dom.clientWidth - total_abs;
9547         
9548         var unitWidth = Math.floor(avail / total);
9549         var rem = avail - (unitWidth * total);
9550         
9551         var hidden, width, pos = 0 , splithide , left;
9552         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9553             
9554             hidden = 'display:none;';
9555             left = '';
9556             width  = 'width:0px;';
9557             splithide = '';
9558             if(!cm.isHidden(i)){
9559                 hidden = '';
9560                 
9561                 
9562                 // we can honour xs/sm/md/xl ?
9563                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9564                 if (w===0) {
9565                     hidden = 'display:none;';
9566                 }
9567                 // width should return a small number...
9568                 if (i == last) {
9569                     w+=rem; // add the remaining with..
9570                 }
9571                 pos += w;
9572                 left = "left:" + (pos -4) + "px;";
9573                 width = "width:" + w+ "px;";
9574                 
9575             }
9576             if (this.responsive) {
9577                 width = '';
9578                 left = '';
9579                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9580                 splithide = 'display: none;';
9581             }
9582             
9583             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9584             if (this.headEl) {
9585                 if (i == last) {
9586                     splithide = 'display:none;';
9587                 }
9588                 
9589                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9590                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9591                             // this is the popover version..
9592                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9593                 );
9594             }
9595             
9596         }
9597         //Roo.log(styles.join(''));
9598         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9599         
9600     },
9601     
9602     
9603     
9604     onContextMenu : function(e, t)
9605     {
9606         this.processEvent("contextmenu", e);
9607     },
9608     
9609     processEvent : function(name, e)
9610     {
9611         if (name != 'touchstart' ) {
9612             this.fireEvent(name, e);    
9613         }
9614         
9615         var t = e.getTarget();
9616         
9617         var cell = Roo.get(t);
9618         
9619         if(!cell){
9620             return;
9621         }
9622         
9623         if(cell.findParent('tfoot', false, true)){
9624             return;
9625         }
9626         
9627         if(cell.findParent('thead', false, true)){
9628             
9629             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9630                 cell = Roo.get(t).findParent('th', false, true);
9631                 if (!cell) {
9632                     Roo.log("failed to find th in thead?");
9633                     Roo.log(e.getTarget());
9634                     return;
9635                 }
9636             }
9637             
9638             var cellIndex = cell.dom.cellIndex;
9639             
9640             var ename = name == 'touchstart' ? 'click' : name;
9641             this.fireEvent("header" + ename, this, cellIndex, e);
9642             
9643             return;
9644         }
9645         
9646         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9647             cell = Roo.get(t).findParent('td', false, true);
9648             if (!cell) {
9649                 Roo.log("failed to find th in tbody?");
9650                 Roo.log(e.getTarget());
9651                 return;
9652             }
9653         }
9654         
9655         var row = cell.findParent('tr', false, true);
9656         var cellIndex = cell.dom.cellIndex;
9657         var rowIndex = row.dom.rowIndex - 1;
9658         
9659         if(row !== false){
9660             
9661             this.fireEvent("row" + name, this, rowIndex, e);
9662             
9663             if(cell !== false){
9664             
9665                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9666             }
9667         }
9668         
9669     },
9670     
9671     onMouseover : function(e, el)
9672     {
9673         var cell = Roo.get(el);
9674         
9675         if(!cell){
9676             return;
9677         }
9678         
9679         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9680             cell = cell.findParent('td', false, true);
9681         }
9682         
9683         var row = cell.findParent('tr', false, true);
9684         var cellIndex = cell.dom.cellIndex;
9685         var rowIndex = row.dom.rowIndex - 1; // start from 0
9686         
9687         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9688         
9689     },
9690     
9691     onMouseout : function(e, el)
9692     {
9693         var cell = Roo.get(el);
9694         
9695         if(!cell){
9696             return;
9697         }
9698         
9699         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9700             cell = cell.findParent('td', false, true);
9701         }
9702         
9703         var row = cell.findParent('tr', false, true);
9704         var cellIndex = cell.dom.cellIndex;
9705         var rowIndex = row.dom.rowIndex - 1; // start from 0
9706         
9707         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9708         
9709     },
9710     
9711     onClick : function(e, el)
9712     {
9713         var cell = Roo.get(el);
9714         
9715         if(!cell || (!this.cellSelection && !this.rowSelection)){
9716             return;
9717         }
9718         
9719         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9720             cell = cell.findParent('td', false, true);
9721         }
9722         
9723         if(!cell || typeof(cell) == 'undefined'){
9724             return;
9725         }
9726         
9727         var row = cell.findParent('tr', false, true);
9728         
9729         if(!row || typeof(row) == 'undefined'){
9730             return;
9731         }
9732         
9733         var cellIndex = cell.dom.cellIndex;
9734         var rowIndex = this.getRowIndex(row);
9735         
9736         // why??? - should these not be based on SelectionModel?
9737         //if(this.cellSelection){
9738             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9739         //}
9740         
9741         //if(this.rowSelection){
9742             this.fireEvent('rowclick', this, row, rowIndex, e);
9743         //}
9744          
9745     },
9746         
9747     onDblClick : function(e,el)
9748     {
9749         var cell = Roo.get(el);
9750         
9751         if(!cell || (!this.cellSelection && !this.rowSelection)){
9752             return;
9753         }
9754         
9755         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9756             cell = cell.findParent('td', false, true);
9757         }
9758         
9759         if(!cell || typeof(cell) == 'undefined'){
9760             return;
9761         }
9762         
9763         var row = cell.findParent('tr', false, true);
9764         
9765         if(!row || typeof(row) == 'undefined'){
9766             return;
9767         }
9768         
9769         var cellIndex = cell.dom.cellIndex;
9770         var rowIndex = this.getRowIndex(row);
9771         
9772         if(this.cellSelection){
9773             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9774         }
9775         
9776         if(this.rowSelection){
9777             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9778         }
9779     },
9780     findRowIndex : function(el)
9781     {
9782         var cell = Roo.get(el);
9783         if(!cell) {
9784             return false;
9785         }
9786         var row = cell.findParent('tr', false, true);
9787         
9788         if(!row || typeof(row) == 'undefined'){
9789             return false;
9790         }
9791         return this.getRowIndex(row);
9792     },
9793     sort : function(e,el)
9794     {
9795         var col = Roo.get(el);
9796         
9797         if(!col.hasClass('sortable')){
9798             return;
9799         }
9800         
9801         var sort = col.attr('sort');
9802         var dir = 'ASC';
9803         
9804         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9805             dir = 'DESC';
9806         }
9807         
9808         this.store.sortInfo = {field : sort, direction : dir};
9809         
9810         if (this.footer) {
9811             Roo.log("calling footer first");
9812             this.footer.onClick('first');
9813         } else {
9814         
9815             this.store.load({ params : { start : 0 } });
9816         }
9817     },
9818     
9819     renderHeader : function()
9820     {
9821         var header = {
9822             tag: 'thead',
9823             cn : []
9824         };
9825         
9826         var cm = this.cm;
9827         this.totalWidth = 0;
9828         
9829         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9830             
9831             var config = cm.config[i];
9832             
9833             var c = {
9834                 tag: 'th',
9835                 cls : 'x-hcol-' + i,
9836                 style : '',
9837                 
9838                 html: cm.getColumnHeader(i)
9839             };
9840             
9841             var tooltip = cm.getColumnTooltip(i);
9842             if (tooltip) {
9843                 c.tooltip = tooltip;
9844             }
9845             
9846             
9847             var hh = '';
9848             
9849             if(typeof(config.sortable) != 'undefined' && config.sortable){
9850                 c.cls += ' sortable';
9851                 c.html = '<i class="fa"></i>' + c.html;
9852             }
9853             
9854             // could use BS4 hidden-..-down 
9855             
9856             if(typeof(config.lgHeader) != 'undefined'){
9857                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9858             }
9859             
9860             if(typeof(config.mdHeader) != 'undefined'){
9861                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9862             }
9863             
9864             if(typeof(config.smHeader) != 'undefined'){
9865                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9866             }
9867             
9868             if(typeof(config.xsHeader) != 'undefined'){
9869                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9870             }
9871             
9872             if(hh.length){
9873                 c.html = hh;
9874             }
9875             
9876             if(typeof(config.tooltip) != 'undefined'){
9877                 c.tooltip = config.tooltip;
9878             }
9879             
9880             if(typeof(config.colspan) != 'undefined'){
9881                 c.colspan = config.colspan;
9882             }
9883             
9884             // hidden is handled by CSS now
9885             
9886             if(typeof(config.dataIndex) != 'undefined'){
9887                 c.sort = config.dataIndex;
9888             }
9889             
9890            
9891             
9892             if(typeof(config.align) != 'undefined' && config.align.length){
9893                 c.style += ' text-align:' + config.align + ';';
9894             }
9895             
9896             /* width is done in CSS
9897              *if(typeof(config.width) != 'undefined'){
9898                 c.style += ' width:' + config.width + 'px;';
9899                 this.totalWidth += config.width;
9900             } else {
9901                 this.totalWidth += 100; // assume minimum of 100 per column?
9902             }
9903             */
9904             
9905             if(typeof(config.cls) != 'undefined'){
9906                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9907             }
9908             // this is the bit that doesnt reall work at all...
9909             
9910             if (this.responsive) {
9911                  
9912             
9913                 ['xs','sm','md','lg'].map(function(size){
9914                     
9915                     if(typeof(config[size]) == 'undefined'){
9916                         return;
9917                     }
9918                      
9919                     if (!config[size]) { // 0 = hidden
9920                         // BS 4 '0' is treated as hide that column and below.
9921                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9922                         return;
9923                     }
9924                     
9925                     c.cls += ' col-' + size + '-' + config[size] + (
9926                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9927                     );
9928                     
9929                     
9930                 });
9931             }
9932             // at the end?
9933             
9934             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9935             
9936             
9937             
9938             
9939             header.cn.push(c)
9940         }
9941         
9942         return header;
9943     },
9944     
9945     renderBody : function()
9946     {
9947         var body = {
9948             tag: 'tbody',
9949             cn : [
9950                 {
9951                     tag: 'tr',
9952                     cn : [
9953                         {
9954                             tag : 'td',
9955                             colspan :  this.cm.getColumnCount()
9956                         }
9957                     ]
9958                 }
9959             ]
9960         };
9961         
9962         return body;
9963     },
9964     
9965     renderFooter : function()
9966     {
9967         var footer = {
9968             tag: 'tfoot',
9969             cn : [
9970                 {
9971                     tag: 'tr',
9972                     cn : [
9973                         {
9974                             tag : 'td',
9975                             colspan :  this.cm.getColumnCount()
9976                         }
9977                     ]
9978                 }
9979             ]
9980         };
9981         
9982         return footer;
9983     },
9984
9985     renderSummaryFooter : function()
9986     {
9987         var footer = {
9988             tag: 'tfoot',
9989             cn : []
9990         };
9991         
9992         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9993             
9994             var c = {
9995                 tag: 'td',
9996                 cls : 'x-fcol-' + i,
9997                 style : '',
9998                 html: ''
9999             };
10000             
10001             footer.cn.push(c)
10002         }
10003         
10004         return footer;
10005     },
10006     
10007     
10008     
10009     onLoad : function()
10010     {
10011 //        Roo.log('ds onload');
10012         this.clear();
10013         
10014         var _this = this;
10015         var cm = this.cm;
10016         var ds = this.store;
10017         
10018         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10019             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10020             if (_this.store.sortInfo) {
10021                     
10022                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10023                     e.select('i', true).addClass(['fa-arrow-up']);
10024                 }
10025                 
10026                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10027                     e.select('i', true).addClass(['fa-arrow-down']);
10028                 }
10029             }
10030         });
10031         
10032         var tbody =  this.bodyEl;
10033               
10034         if(ds.getCount() > 0){
10035             ds.data.each(function(d,rowIndex){
10036                 var row =  this.renderRow(cm, ds, rowIndex);
10037                 
10038                 tbody.createChild(row);
10039                 
10040                 var _this = this;
10041                 
10042                 if(row.cellObjects.length){
10043                     Roo.each(row.cellObjects, function(r){
10044                         _this.renderCellObject(r);
10045                     })
10046                 }
10047                 
10048             }, this);
10049         } else if (this.empty_results.length) {
10050             this.el.mask(this.empty_results, 'no-spinner');
10051         }
10052         
10053         var tfoot = this.el.select('tfoot', true).first();
10054         
10055         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10056             
10057             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10058             
10059             var total = this.ds.getTotalCount();
10060             
10061             if(this.footer.pageSize < total){
10062                 this.mainFoot.show();
10063             }
10064         }
10065
10066         if(!this.footerShow && this.summaryFooterShow) {
10067
10068             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10069         
10070                 var value = cm.config[i].summaryFooter;
10071
10072                 Roo.log('value [' + i + '] : ' + value);
10073             }
10074         }
10075         
10076         Roo.each(this.el.select('tbody td', true).elements, function(e){
10077             e.on('mouseover', _this.onMouseover, _this);
10078         });
10079         
10080         Roo.each(this.el.select('tbody td', true).elements, function(e){
10081             e.on('mouseout', _this.onMouseout, _this);
10082         });
10083         this.fireEvent('rowsrendered', this);
10084         
10085         this.autoSize();
10086         
10087         this.initCSS(); /// resize cols
10088
10089         
10090     },
10091     
10092     
10093     onUpdate : function(ds,record)
10094     {
10095         this.refreshRow(record);
10096         this.autoSize();
10097     },
10098     
10099     onRemove : function(ds, record, index, isUpdate){
10100         if(isUpdate !== true){
10101             this.fireEvent("beforerowremoved", this, index, record);
10102         }
10103         var bt = this.bodyEl.dom;
10104         
10105         var rows = this.el.select('tbody > tr', true).elements;
10106         
10107         if(typeof(rows[index]) != 'undefined'){
10108             bt.removeChild(rows[index].dom);
10109         }
10110         
10111 //        if(bt.rows[index]){
10112 //            bt.removeChild(bt.rows[index]);
10113 //        }
10114         
10115         if(isUpdate !== true){
10116             //this.stripeRows(index);
10117             //this.syncRowHeights(index, index);
10118             //this.layout();
10119             this.fireEvent("rowremoved", this, index, record);
10120         }
10121     },
10122     
10123     onAdd : function(ds, records, rowIndex)
10124     {
10125         //Roo.log('on Add called');
10126         // - note this does not handle multiple adding very well..
10127         var bt = this.bodyEl.dom;
10128         for (var i =0 ; i < records.length;i++) {
10129             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10130             //Roo.log(records[i]);
10131             //Roo.log(this.store.getAt(rowIndex+i));
10132             this.insertRow(this.store, rowIndex + i, false);
10133             return;
10134         }
10135         
10136     },
10137     
10138     
10139     refreshRow : function(record){
10140         var ds = this.store, index;
10141         if(typeof record == 'number'){
10142             index = record;
10143             record = ds.getAt(index);
10144         }else{
10145             index = ds.indexOf(record);
10146             if (index < 0) {
10147                 return; // should not happen - but seems to 
10148             }
10149         }
10150         this.insertRow(ds, index, true);
10151         this.autoSize();
10152         this.onRemove(ds, record, index+1, true);
10153         this.autoSize();
10154         //this.syncRowHeights(index, index);
10155         //this.layout();
10156         this.fireEvent("rowupdated", this, index, record);
10157     },
10158     // private - called by RowSelection
10159     onRowSelect : function(rowIndex){
10160         var row = this.getRowDom(rowIndex);
10161         row.addClass(['bg-info','info']);
10162     },
10163     // private - called by RowSelection
10164     onRowDeselect : function(rowIndex)
10165     {
10166         if (rowIndex < 0) {
10167             return;
10168         }
10169         var row = this.getRowDom(rowIndex);
10170         row.removeClass(['bg-info','info']);
10171     },
10172       /**
10173      * Focuses the specified row.
10174      * @param {Number} row The row index
10175      */
10176     focusRow : function(row)
10177     {
10178         //Roo.log('GridView.focusRow');
10179         var x = this.bodyEl.dom.scrollLeft;
10180         this.focusCell(row, 0, false);
10181         this.bodyEl.dom.scrollLeft = x;
10182
10183     },
10184      /**
10185      * Focuses the specified cell.
10186      * @param {Number} row The row index
10187      * @param {Number} col The column index
10188      * @param {Boolean} hscroll false to disable horizontal scrolling
10189      */
10190     focusCell : function(row, col, hscroll)
10191     {
10192         //Roo.log('GridView.focusCell');
10193         var el = this.ensureVisible(row, col, hscroll);
10194         // not sure what focusEL achives = it's a <a> pos relative 
10195         //this.focusEl.alignTo(el, "tl-tl");
10196         //if(Roo.isGecko){
10197         //    this.focusEl.focus();
10198         //}else{
10199         //    this.focusEl.focus.defer(1, this.focusEl);
10200         //}
10201     },
10202     
10203      /**
10204      * Scrolls the specified cell into view
10205      * @param {Number} row The row index
10206      * @param {Number} col The column index
10207      * @param {Boolean} hscroll false to disable horizontal scrolling
10208      */
10209     ensureVisible : function(row, col, hscroll)
10210     {
10211         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10212         //return null; //disable for testing.
10213         if(typeof row != "number"){
10214             row = row.rowIndex;
10215         }
10216         if(row < 0 && row >= this.ds.getCount()){
10217             return  null;
10218         }
10219         col = (col !== undefined ? col : 0);
10220         var cm = this.cm;
10221         while(cm.isHidden(col)){
10222             col++;
10223         }
10224
10225         var el = this.getCellDom(row, col);
10226         if(!el){
10227             return null;
10228         }
10229         var c = this.bodyEl.dom;
10230
10231         var ctop = parseInt(el.offsetTop, 10);
10232         var cleft = parseInt(el.offsetLeft, 10);
10233         var cbot = ctop + el.offsetHeight;
10234         var cright = cleft + el.offsetWidth;
10235
10236         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10237         var ch = 0; //?? header is not withing the area?
10238         var stop = parseInt(c.scrollTop, 10);
10239         var sleft = parseInt(c.scrollLeft, 10);
10240         var sbot = stop + ch;
10241         var sright = sleft + c.clientWidth;
10242         /*
10243         Roo.log('GridView.ensureVisible:' +
10244                 ' ctop:' + ctop +
10245                 ' c.clientHeight:' + c.clientHeight +
10246                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10247                 ' stop:' + stop +
10248                 ' cbot:' + cbot +
10249                 ' sbot:' + sbot +
10250                 ' ch:' + ch  
10251                 );
10252         */
10253         if(ctop < stop){
10254             c.scrollTop = ctop;
10255             //Roo.log("set scrolltop to ctop DISABLE?");
10256         }else if(cbot > sbot){
10257             //Roo.log("set scrolltop to cbot-ch");
10258             c.scrollTop = cbot-ch;
10259         }
10260
10261         if(hscroll !== false){
10262             if(cleft < sleft){
10263                 c.scrollLeft = cleft;
10264             }else if(cright > sright){
10265                 c.scrollLeft = cright-c.clientWidth;
10266             }
10267         }
10268
10269         return el;
10270     },
10271     
10272     
10273     insertRow : function(dm, rowIndex, isUpdate){
10274         
10275         if(!isUpdate){
10276             this.fireEvent("beforerowsinserted", this, rowIndex);
10277         }
10278             //var s = this.getScrollState();
10279         var row = this.renderRow(this.cm, this.store, rowIndex);
10280         // insert before rowIndex..
10281         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10282         
10283         var _this = this;
10284                 
10285         if(row.cellObjects.length){
10286             Roo.each(row.cellObjects, function(r){
10287                 _this.renderCellObject(r);
10288             })
10289         }
10290             
10291         if(!isUpdate){
10292             this.fireEvent("rowsinserted", this, rowIndex);
10293             //this.syncRowHeights(firstRow, lastRow);
10294             //this.stripeRows(firstRow);
10295             //this.layout();
10296         }
10297         
10298     },
10299     
10300     
10301     getRowDom : function(rowIndex)
10302     {
10303         var rows = this.el.select('tbody > tr', true).elements;
10304         
10305         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10306         
10307     },
10308     getCellDom : function(rowIndex, colIndex)
10309     {
10310         var row = this.getRowDom(rowIndex);
10311         if (row === false) {
10312             return false;
10313         }
10314         var cols = row.select('td', true).elements;
10315         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10316         
10317     },
10318     
10319     // returns the object tree for a tr..
10320   
10321     
10322     renderRow : function(cm, ds, rowIndex) 
10323     {
10324         var d = ds.getAt(rowIndex);
10325         
10326         var row = {
10327             tag : 'tr',
10328             cls : 'x-row-' + rowIndex,
10329             cn : []
10330         };
10331             
10332         var cellObjects = [];
10333         
10334         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10335             var config = cm.config[i];
10336             
10337             var renderer = cm.getRenderer(i);
10338             var value = '';
10339             var id = false;
10340             
10341             if(typeof(renderer) !== 'undefined'){
10342                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10343             }
10344             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10345             // and are rendered into the cells after the row is rendered - using the id for the element.
10346             
10347             if(typeof(value) === 'object'){
10348                 id = Roo.id();
10349                 cellObjects.push({
10350                     container : id,
10351                     cfg : value 
10352                 })
10353             }
10354             
10355             var rowcfg = {
10356                 record: d,
10357                 rowIndex : rowIndex,
10358                 colIndex : i,
10359                 rowClass : ''
10360             };
10361
10362             this.fireEvent('rowclass', this, rowcfg);
10363             
10364             var td = {
10365                 tag: 'td',
10366                 // this might end up displaying HTML?
10367                 // this is too messy... - better to only do it on columsn you know are going to be too long
10368                 //tooltip : (typeof(value) === 'object') ? '' : value,
10369                 cls : rowcfg.rowClass + ' x-col-' + i,
10370                 style: '',
10371                 html: (typeof(value) === 'object') ? '' : value
10372             };
10373             
10374             if (id) {
10375                 td.id = id;
10376             }
10377             
10378             if(typeof(config.colspan) != 'undefined'){
10379                 td.colspan = config.colspan;
10380             }
10381             
10382             
10383             
10384             if(typeof(config.align) != 'undefined' && config.align.length){
10385                 td.style += ' text-align:' + config.align + ';';
10386             }
10387             if(typeof(config.valign) != 'undefined' && config.valign.length){
10388                 td.style += ' vertical-align:' + config.valign + ';';
10389             }
10390             /*
10391             if(typeof(config.width) != 'undefined'){
10392                 td.style += ' width:' +  config.width + 'px;';
10393             }
10394             */
10395             
10396             if(typeof(config.cursor) != 'undefined'){
10397                 td.style += ' cursor:' +  config.cursor + ';';
10398             }
10399             
10400             if(typeof(config.cls) != 'undefined'){
10401                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10402             }
10403             if (this.responsive) {
10404                 ['xs','sm','md','lg'].map(function(size){
10405                     
10406                     if(typeof(config[size]) == 'undefined'){
10407                         return;
10408                     }
10409                     
10410                     
10411                       
10412                     if (!config[size]) { // 0 = hidden
10413                         // BS 4 '0' is treated as hide that column and below.
10414                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10415                         return;
10416                     }
10417                     
10418                     td.cls += ' col-' + size + '-' + config[size] + (
10419                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10420                     );
10421                      
10422     
10423                 });
10424             }
10425             row.cn.push(td);
10426            
10427         }
10428         
10429         row.cellObjects = cellObjects;
10430         
10431         return row;
10432           
10433     },
10434     
10435     
10436     
10437     onBeforeLoad : function()
10438     {
10439         this.el.unmask(); // if needed.
10440     },
10441      /**
10442      * Remove all rows
10443      */
10444     clear : function()
10445     {
10446         this.el.select('tbody', true).first().dom.innerHTML = '';
10447     },
10448     /**
10449      * Show or hide a row.
10450      * @param {Number} rowIndex to show or hide
10451      * @param {Boolean} state hide
10452      */
10453     setRowVisibility : function(rowIndex, state)
10454     {
10455         var bt = this.bodyEl.dom;
10456         
10457         var rows = this.el.select('tbody > tr', true).elements;
10458         
10459         if(typeof(rows[rowIndex]) == 'undefined'){
10460             return;
10461         }
10462         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10463         
10464     },
10465     
10466     
10467     getSelectionModel : function(){
10468         if(!this.selModel){
10469             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10470         }
10471         return this.selModel;
10472     },
10473     /*
10474      * Render the Roo.bootstrap object from renderder
10475      */
10476     renderCellObject : function(r)
10477     {
10478         var _this = this;
10479         
10480         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10481         
10482         var t = r.cfg.render(r.container);
10483         
10484         if(r.cfg.cn){
10485             Roo.each(r.cfg.cn, function(c){
10486                 var child = {
10487                     container: t.getChildContainer(),
10488                     cfg: c
10489                 };
10490                 _this.renderCellObject(child);
10491             })
10492         }
10493     },
10494     /**
10495      * get the Row Index from a dom element.
10496      * @param {Roo.Element} row The row to look for
10497      * @returns {Number} the row
10498      */
10499     getRowIndex : function(row)
10500     {
10501         var rowIndex = -1;
10502         
10503         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10504             if(el != row){
10505                 return;
10506             }
10507             
10508             rowIndex = index;
10509         });
10510         
10511         return rowIndex;
10512     },
10513     /**
10514      * get the header TH element for columnIndex
10515      * @param {Number} columnIndex
10516      * @returns {Roo.Element}
10517      */
10518     getHeaderIndex: function(colIndex)
10519     {
10520         var cols = this.headEl.select('th', true).elements;
10521         return cols[colIndex]; 
10522     },
10523     /**
10524      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10525      * @param {domElement} cell to look for
10526      * @returns {Number} the column
10527      */
10528     getCellIndex : function(cell)
10529     {
10530         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10531         if(id){
10532             return parseInt(id[1], 10);
10533         }
10534         return 0;
10535     },
10536      /**
10537      * Returns the grid's underlying element = used by panel.Grid
10538      * @return {Element} The element
10539      */
10540     getGridEl : function(){
10541         return this.el;
10542     },
10543      /**
10544      * Forces a resize - used by panel.Grid
10545      * @return {Element} The element
10546      */
10547     autoSize : function()
10548     {
10549         if(this.disableAutoSize) {
10550             return;
10551         }
10552         //var ctr = Roo.get(this.container.dom.parentElement);
10553         var ctr = Roo.get(this.el.dom);
10554         
10555         var thd = this.getGridEl().select('thead',true).first();
10556         var tbd = this.getGridEl().select('tbody', true).first();
10557         var tfd = this.getGridEl().select('tfoot', true).first();
10558         
10559         var cw = ctr.getWidth();
10560         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10561         
10562         if (tbd) {
10563             
10564             tbd.setWidth(ctr.getWidth());
10565             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10566             // this needs fixing for various usage - currently only hydra job advers I think..
10567             //tdb.setHeight(
10568             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10569             //); 
10570             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10571             cw -= barsize;
10572         }
10573         cw = Math.max(cw, this.totalWidth);
10574         this.getGridEl().select('tbody tr',true).setWidth(cw);
10575         this.initCSS();
10576         
10577         // resize 'expandable coloumn?
10578         
10579         return; // we doe not have a view in this design..
10580         
10581     },
10582     onBodyScroll: function()
10583     {
10584         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10585         if(this.headEl){
10586             this.headEl.setStyle({
10587                 'position' : 'relative',
10588                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10589             });
10590         }
10591         
10592         if(this.lazyLoad){
10593             
10594             var scrollHeight = this.bodyEl.dom.scrollHeight;
10595             
10596             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10597             
10598             var height = this.bodyEl.getHeight();
10599             
10600             if(scrollHeight - height == scrollTop) {
10601                 
10602                 var total = this.ds.getTotalCount();
10603                 
10604                 if(this.footer.cursor + this.footer.pageSize < total){
10605                     
10606                     this.footer.ds.load({
10607                         params : {
10608                             start : this.footer.cursor + this.footer.pageSize,
10609                             limit : this.footer.pageSize
10610                         },
10611                         add : true
10612                     });
10613                 }
10614             }
10615             
10616         }
10617     },
10618     onColumnSplitterMoved : function(i, diff)
10619     {
10620         this.userResized = true;
10621         
10622         var cm = this.colModel;
10623         
10624         var w = this.getHeaderIndex(i).getWidth() + diff;
10625         
10626         
10627         cm.setColumnWidth(i, w, true);
10628         this.initCSS();
10629         //var cid = cm.getColumnId(i); << not used in this version?
10630        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10631         
10632         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10633         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10634         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10635 */
10636         //this.updateSplitters();
10637         //this.layout(); << ??
10638         this.fireEvent("columnresize", i, w);
10639     },
10640     onHeaderChange : function()
10641     {
10642         var header = this.renderHeader();
10643         var table = this.el.select('table', true).first();
10644         
10645         this.headEl.remove();
10646         this.headEl = table.createChild(header, this.bodyEl, false);
10647         
10648         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10649             e.on('click', this.sort, this);
10650         }, this);
10651         
10652         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10653             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10654         }
10655         
10656     },
10657     
10658     onHiddenChange : function(colModel, colIndex, hidden)
10659     {
10660         /*
10661         this.cm.setHidden()
10662         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10663         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10664         
10665         this.CSS.updateRule(thSelector, "display", "");
10666         this.CSS.updateRule(tdSelector, "display", "");
10667         
10668         if(hidden){
10669             this.CSS.updateRule(thSelector, "display", "none");
10670             this.CSS.updateRule(tdSelector, "display", "none");
10671         }
10672         */
10673         // onload calls initCSS()
10674         this.onHeaderChange();
10675         this.onLoad();
10676     },
10677     
10678     setColumnWidth: function(col_index, width)
10679     {
10680         // width = "md-2 xs-2..."
10681         if(!this.colModel.config[col_index]) {
10682             return;
10683         }
10684         
10685         var w = width.split(" ");
10686         
10687         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10688         
10689         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10690         
10691         
10692         for(var j = 0; j < w.length; j++) {
10693             
10694             if(!w[j]) {
10695                 continue;
10696             }
10697             
10698             var size_cls = w[j].split("-");
10699             
10700             if(!Number.isInteger(size_cls[1] * 1)) {
10701                 continue;
10702             }
10703             
10704             if(!this.colModel.config[col_index][size_cls[0]]) {
10705                 continue;
10706             }
10707             
10708             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10709                 continue;
10710             }
10711             
10712             h_row[0].classList.replace(
10713                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10714                 "col-"+size_cls[0]+"-"+size_cls[1]
10715             );
10716             
10717             for(var i = 0; i < rows.length; i++) {
10718                 
10719                 var size_cls = w[j].split("-");
10720                 
10721                 if(!Number.isInteger(size_cls[1] * 1)) {
10722                     continue;
10723                 }
10724                 
10725                 if(!this.colModel.config[col_index][size_cls[0]]) {
10726                     continue;
10727                 }
10728                 
10729                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10730                     continue;
10731                 }
10732                 
10733                 rows[i].classList.replace(
10734                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10735                     "col-"+size_cls[0]+"-"+size_cls[1]
10736                 );
10737             }
10738             
10739             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10740         }
10741     }
10742 });
10743
10744 // currently only used to find the split on drag.. 
10745 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10746
10747 /**
10748  * @depricated
10749 */
10750 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10751 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10752 /*
10753  * - LGPL
10754  *
10755  * table cell
10756  * 
10757  */
10758
10759 /**
10760  * @class Roo.bootstrap.TableCell
10761  * @extends Roo.bootstrap.Component
10762  * @children Roo.bootstrap.Component
10763  * @parent Roo.bootstrap.TableRow
10764  * Bootstrap TableCell class
10765  * 
10766  * @cfg {String} html cell contain text
10767  * @cfg {String} cls cell class
10768  * @cfg {String} tag cell tag (td|th) default td
10769  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10770  * @cfg {String} align Aligns the content in a cell
10771  * @cfg {String} axis Categorizes cells
10772  * @cfg {String} bgcolor Specifies the background color of a cell
10773  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10774  * @cfg {Number} colspan Specifies the number of columns a cell should span
10775  * @cfg {String} headers Specifies one or more header cells a cell is related to
10776  * @cfg {Number} height Sets the height of a cell
10777  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10778  * @cfg {Number} rowspan Sets the number of rows a cell should span
10779  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10780  * @cfg {String} valign Vertical aligns the content in a cell
10781  * @cfg {Number} width Specifies the width of a cell
10782  * 
10783  * @constructor
10784  * Create a new TableCell
10785  * @param {Object} config The config object
10786  */
10787
10788 Roo.bootstrap.TableCell = function(config){
10789     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10790 };
10791
10792 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10793     
10794     html: false,
10795     cls: false,
10796     tag: false,
10797     abbr: false,
10798     align: false,
10799     axis: false,
10800     bgcolor: false,
10801     charoff: false,
10802     colspan: false,
10803     headers: false,
10804     height: false,
10805     nowrap: false,
10806     rowspan: false,
10807     scope: false,
10808     valign: false,
10809     width: false,
10810     
10811     
10812     getAutoCreate : function(){
10813         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10814         
10815         cfg = {
10816             tag: 'td'
10817         };
10818         
10819         if(this.tag){
10820             cfg.tag = this.tag;
10821         }
10822         
10823         if (this.html) {
10824             cfg.html=this.html
10825         }
10826         if (this.cls) {
10827             cfg.cls=this.cls
10828         }
10829         if (this.abbr) {
10830             cfg.abbr=this.abbr
10831         }
10832         if (this.align) {
10833             cfg.align=this.align
10834         }
10835         if (this.axis) {
10836             cfg.axis=this.axis
10837         }
10838         if (this.bgcolor) {
10839             cfg.bgcolor=this.bgcolor
10840         }
10841         if (this.charoff) {
10842             cfg.charoff=this.charoff
10843         }
10844         if (this.colspan) {
10845             cfg.colspan=this.colspan
10846         }
10847         if (this.headers) {
10848             cfg.headers=this.headers
10849         }
10850         if (this.height) {
10851             cfg.height=this.height
10852         }
10853         if (this.nowrap) {
10854             cfg.nowrap=this.nowrap
10855         }
10856         if (this.rowspan) {
10857             cfg.rowspan=this.rowspan
10858         }
10859         if (this.scope) {
10860             cfg.scope=this.scope
10861         }
10862         if (this.valign) {
10863             cfg.valign=this.valign
10864         }
10865         if (this.width) {
10866             cfg.width=this.width
10867         }
10868         
10869         
10870         return cfg;
10871     }
10872    
10873 });
10874
10875  
10876
10877  /*
10878  * - LGPL
10879  *
10880  * table row
10881  * 
10882  */
10883
10884 /**
10885  * @class Roo.bootstrap.TableRow
10886  * @extends Roo.bootstrap.Component
10887  * @children Roo.bootstrap.TableCell
10888  * @parent Roo.bootstrap.TableBody
10889  * Bootstrap TableRow class
10890  * @cfg {String} cls row class
10891  * @cfg {String} align Aligns the content in a table row
10892  * @cfg {String} bgcolor Specifies a background color for a table row
10893  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10894  * @cfg {String} valign Vertical aligns the content in a table row
10895  * 
10896  * @constructor
10897  * Create a new TableRow
10898  * @param {Object} config The config object
10899  */
10900
10901 Roo.bootstrap.TableRow = function(config){
10902     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10903 };
10904
10905 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10906     
10907     cls: false,
10908     align: false,
10909     bgcolor: false,
10910     charoff: false,
10911     valign: false,
10912     
10913     getAutoCreate : function(){
10914         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10915         
10916         cfg = {
10917             tag: 'tr'
10918         };
10919             
10920         if(this.cls){
10921             cfg.cls = this.cls;
10922         }
10923         if(this.align){
10924             cfg.align = this.align;
10925         }
10926         if(this.bgcolor){
10927             cfg.bgcolor = this.bgcolor;
10928         }
10929         if(this.charoff){
10930             cfg.charoff = this.charoff;
10931         }
10932         if(this.valign){
10933             cfg.valign = this.valign;
10934         }
10935         
10936         return cfg;
10937     }
10938    
10939 });
10940
10941  
10942
10943  /*
10944  * - LGPL
10945  *
10946  * table body
10947  * 
10948  */
10949
10950 /**
10951  * @class Roo.bootstrap.TableBody
10952  * @extends Roo.bootstrap.Component
10953  * @children Roo.bootstrap.TableRow
10954  * @parent Roo.bootstrap.Table
10955  * Bootstrap TableBody class
10956  * @cfg {String} cls element class
10957  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10958  * @cfg {String} align Aligns the content inside the element
10959  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10960  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10961  * 
10962  * @constructor
10963  * Create a new TableBody
10964  * @param {Object} config The config object
10965  */
10966
10967 Roo.bootstrap.TableBody = function(config){
10968     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10969 };
10970
10971 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10972     
10973     cls: false,
10974     tag: false,
10975     align: false,
10976     charoff: false,
10977     valign: false,
10978     
10979     getAutoCreate : function(){
10980         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10981         
10982         cfg = {
10983             tag: 'tbody'
10984         };
10985             
10986         if (this.cls) {
10987             cfg.cls=this.cls
10988         }
10989         if(this.tag){
10990             cfg.tag = this.tag;
10991         }
10992         
10993         if(this.align){
10994             cfg.align = this.align;
10995         }
10996         if(this.charoff){
10997             cfg.charoff = this.charoff;
10998         }
10999         if(this.valign){
11000             cfg.valign = this.valign;
11001         }
11002         
11003         return cfg;
11004     }
11005     
11006     
11007 //    initEvents : function()
11008 //    {
11009 //        
11010 //        if(!this.store){
11011 //            return;
11012 //        }
11013 //        
11014 //        this.store = Roo.factory(this.store, Roo.data);
11015 //        this.store.on('load', this.onLoad, this);
11016 //        
11017 //        this.store.load();
11018 //        
11019 //    },
11020 //    
11021 //    onLoad: function () 
11022 //    {   
11023 //        this.fireEvent('load', this);
11024 //    }
11025 //    
11026 //   
11027 });
11028
11029  
11030
11031  /*
11032  * Based on:
11033  * Ext JS Library 1.1.1
11034  * Copyright(c) 2006-2007, Ext JS, LLC.
11035  *
11036  * Originally Released Under LGPL - original licence link has changed is not relivant.
11037  *
11038  * Fork - LGPL
11039  * <script type="text/javascript">
11040  */
11041
11042 // as we use this in bootstrap.
11043 Roo.namespace('Roo.form');
11044  /**
11045  * @class Roo.form.Action
11046  * Internal Class used to handle form actions
11047  * @constructor
11048  * @param {Roo.form.BasicForm} el The form element or its id
11049  * @param {Object} config Configuration options
11050  */
11051
11052  
11053  
11054 // define the action interface
11055 Roo.form.Action = function(form, options){
11056     this.form = form;
11057     this.options = options || {};
11058 };
11059 /**
11060  * Client Validation Failed
11061  * @const 
11062  */
11063 Roo.form.Action.CLIENT_INVALID = 'client';
11064 /**
11065  * Server Validation Failed
11066  * @const 
11067  */
11068 Roo.form.Action.SERVER_INVALID = 'server';
11069  /**
11070  * Connect to Server Failed
11071  * @const 
11072  */
11073 Roo.form.Action.CONNECT_FAILURE = 'connect';
11074 /**
11075  * Reading Data from Server Failed
11076  * @const 
11077  */
11078 Roo.form.Action.LOAD_FAILURE = 'load';
11079
11080 Roo.form.Action.prototype = {
11081     type : 'default',
11082     failureType : undefined,
11083     response : undefined,
11084     result : undefined,
11085
11086     // interface method
11087     run : function(options){
11088
11089     },
11090
11091     // interface method
11092     success : function(response){
11093
11094     },
11095
11096     // interface method
11097     handleResponse : function(response){
11098
11099     },
11100
11101     // default connection failure
11102     failure : function(response){
11103         
11104         this.response = response;
11105         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11106         this.form.afterAction(this, false);
11107     },
11108
11109     processResponse : function(response){
11110         this.response = response;
11111         if(!response.responseText){
11112             return true;
11113         }
11114         this.result = this.handleResponse(response);
11115         return this.result;
11116     },
11117
11118     // utility functions used internally
11119     getUrl : function(appendParams){
11120         var url = this.options.url || this.form.url || this.form.el.dom.action;
11121         if(appendParams){
11122             var p = this.getParams();
11123             if(p){
11124                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11125             }
11126         }
11127         return url;
11128     },
11129
11130     getMethod : function(){
11131         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11132     },
11133
11134     getParams : function(){
11135         var bp = this.form.baseParams;
11136         var p = this.options.params;
11137         if(p){
11138             if(typeof p == "object"){
11139                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11140             }else if(typeof p == 'string' && bp){
11141                 p += '&' + Roo.urlEncode(bp);
11142             }
11143         }else if(bp){
11144             p = Roo.urlEncode(bp);
11145         }
11146         return p;
11147     },
11148
11149     createCallback : function(){
11150         return {
11151             success: this.success,
11152             failure: this.failure,
11153             scope: this,
11154             timeout: (this.form.timeout*1000),
11155             upload: this.form.fileUpload ? this.success : undefined
11156         };
11157     }
11158 };
11159
11160 Roo.form.Action.Submit = function(form, options){
11161     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11162 };
11163
11164 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11165     type : 'submit',
11166
11167     haveProgress : false,
11168     uploadComplete : false,
11169     
11170     // uploadProgress indicator.
11171     uploadProgress : function()
11172     {
11173         if (!this.form.progressUrl) {
11174             return;
11175         }
11176         
11177         if (!this.haveProgress) {
11178             Roo.MessageBox.progress("Uploading", "Uploading");
11179         }
11180         if (this.uploadComplete) {
11181            Roo.MessageBox.hide();
11182            return;
11183         }
11184         
11185         this.haveProgress = true;
11186    
11187         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11188         
11189         var c = new Roo.data.Connection();
11190         c.request({
11191             url : this.form.progressUrl,
11192             params: {
11193                 id : uid
11194             },
11195             method: 'GET',
11196             success : function(req){
11197                //console.log(data);
11198                 var rdata = false;
11199                 var edata;
11200                 try  {
11201                    rdata = Roo.decode(req.responseText)
11202                 } catch (e) {
11203                     Roo.log("Invalid data from server..");
11204                     Roo.log(edata);
11205                     return;
11206                 }
11207                 if (!rdata || !rdata.success) {
11208                     Roo.log(rdata);
11209                     Roo.MessageBox.alert(Roo.encode(rdata));
11210                     return;
11211                 }
11212                 var data = rdata.data;
11213                 
11214                 if (this.uploadComplete) {
11215                    Roo.MessageBox.hide();
11216                    return;
11217                 }
11218                    
11219                 if (data){
11220                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11221                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11222                     );
11223                 }
11224                 this.uploadProgress.defer(2000,this);
11225             },
11226        
11227             failure: function(data) {
11228                 Roo.log('progress url failed ');
11229                 Roo.log(data);
11230             },
11231             scope : this
11232         });
11233            
11234     },
11235     
11236     
11237     run : function()
11238     {
11239         // run get Values on the form, so it syncs any secondary forms.
11240         this.form.getValues();
11241         
11242         var o = this.options;
11243         var method = this.getMethod();
11244         var isPost = method == 'POST';
11245         if(o.clientValidation === false || this.form.isValid()){
11246             
11247             if (this.form.progressUrl) {
11248                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11249                     (new Date() * 1) + '' + Math.random());
11250                     
11251             } 
11252             
11253             
11254             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11255                 form:this.form.el.dom,
11256                 url:this.getUrl(!isPost),
11257                 method: method,
11258                 params:isPost ? this.getParams() : null,
11259                 isUpload: this.form.fileUpload,
11260                 formData : this.form.formData
11261             }));
11262             
11263             this.uploadProgress();
11264
11265         }else if (o.clientValidation !== false){ // client validation failed
11266             this.failureType = Roo.form.Action.CLIENT_INVALID;
11267             this.form.afterAction(this, false);
11268         }
11269     },
11270
11271     success : function(response)
11272     {
11273         this.uploadComplete= true;
11274         if (this.haveProgress) {
11275             Roo.MessageBox.hide();
11276         }
11277         
11278         
11279         var result = this.processResponse(response);
11280         if(result === true || result.success){
11281             this.form.afterAction(this, true);
11282             return;
11283         }
11284         if(result.errors){
11285             this.form.markInvalid(result.errors);
11286             this.failureType = Roo.form.Action.SERVER_INVALID;
11287         }
11288         this.form.afterAction(this, false);
11289     },
11290     failure : function(response)
11291     {
11292         this.uploadComplete= true;
11293         if (this.haveProgress) {
11294             Roo.MessageBox.hide();
11295         }
11296         
11297         this.response = response;
11298         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11299         this.form.afterAction(this, false);
11300     },
11301     
11302     handleResponse : function(response){
11303         if(this.form.errorReader){
11304             var rs = this.form.errorReader.read(response);
11305             var errors = [];
11306             if(rs.records){
11307                 for(var i = 0, len = rs.records.length; i < len; i++) {
11308                     var r = rs.records[i];
11309                     errors[i] = r.data;
11310                 }
11311             }
11312             if(errors.length < 1){
11313                 errors = null;
11314             }
11315             return {
11316                 success : rs.success,
11317                 errors : errors
11318             };
11319         }
11320         var ret = false;
11321         try {
11322             ret = Roo.decode(response.responseText);
11323         } catch (e) {
11324             ret = {
11325                 success: false,
11326                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11327                 errors : []
11328             };
11329         }
11330         return ret;
11331         
11332     }
11333 });
11334
11335
11336 Roo.form.Action.Load = function(form, options){
11337     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11338     this.reader = this.form.reader;
11339 };
11340
11341 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11342     type : 'load',
11343
11344     run : function(){
11345         
11346         Roo.Ajax.request(Roo.apply(
11347                 this.createCallback(), {
11348                     method:this.getMethod(),
11349                     url:this.getUrl(false),
11350                     params:this.getParams()
11351         }));
11352     },
11353
11354     success : function(response){
11355         
11356         var result = this.processResponse(response);
11357         if(result === true || !result.success || !result.data){
11358             this.failureType = Roo.form.Action.LOAD_FAILURE;
11359             this.form.afterAction(this, false);
11360             return;
11361         }
11362         this.form.clearInvalid();
11363         this.form.setValues(result.data);
11364         this.form.afterAction(this, true);
11365     },
11366
11367     handleResponse : function(response){
11368         if(this.form.reader){
11369             var rs = this.form.reader.read(response);
11370             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11371             return {
11372                 success : rs.success,
11373                 data : data
11374             };
11375         }
11376         return Roo.decode(response.responseText);
11377     }
11378 });
11379
11380 Roo.form.Action.ACTION_TYPES = {
11381     'load' : Roo.form.Action.Load,
11382     'submit' : Roo.form.Action.Submit
11383 };/*
11384  * - LGPL
11385  *
11386  * form
11387  *
11388  */
11389
11390 /**
11391  * @class Roo.bootstrap.form.Form
11392  * @extends Roo.bootstrap.Component
11393  * @children Roo.bootstrap.Component
11394  * Bootstrap Form class
11395  * @cfg {String} method  GET | POST (default POST)
11396  * @cfg {String} labelAlign top | left (default top)
11397  * @cfg {String} align left  | right - for navbars
11398  * @cfg {Boolean} loadMask load mask when submit (default true)
11399
11400  *
11401  * @constructor
11402  * Create a new Form
11403  * @param {Object} config The config object
11404  */
11405
11406
11407 Roo.bootstrap.form.Form = function(config){
11408     
11409     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11410     
11411     Roo.bootstrap.form.Form.popover.apply();
11412     
11413     this.addEvents({
11414         /**
11415          * @event clientvalidation
11416          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11417          * @param {Form} this
11418          * @param {Boolean} valid true if the form has passed client-side validation
11419          */
11420         clientvalidation: true,
11421         /**
11422          * @event beforeaction
11423          * Fires before any action is performed. Return false to cancel the action.
11424          * @param {Form} this
11425          * @param {Action} action The action to be performed
11426          */
11427         beforeaction: true,
11428         /**
11429          * @event actionfailed
11430          * Fires when an action fails.
11431          * @param {Form} this
11432          * @param {Action} action The action that failed
11433          */
11434         actionfailed : true,
11435         /**
11436          * @event actioncomplete
11437          * Fires when an action is completed.
11438          * @param {Form} this
11439          * @param {Action} action The action that completed
11440          */
11441         actioncomplete : true
11442     });
11443 };
11444
11445 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11446
11447      /**
11448      * @cfg {String} method
11449      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11450      */
11451     method : 'POST',
11452     /**
11453      * @cfg {String} url
11454      * The URL to use for form actions if one isn't supplied in the action options.
11455      */
11456     /**
11457      * @cfg {Boolean} fileUpload
11458      * Set to true if this form is a file upload.
11459      */
11460
11461     /**
11462      * @cfg {Object} baseParams
11463      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11464      */
11465
11466     /**
11467      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11468      */
11469     timeout: 30,
11470     /**
11471      * @cfg {Sting} align (left|right) for navbar forms
11472      */
11473     align : 'left',
11474
11475     // private
11476     activeAction : null,
11477
11478     /**
11479      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11480      * element by passing it or its id or mask the form itself by passing in true.
11481      * @type Mixed
11482      */
11483     waitMsgTarget : false,
11484
11485     loadMask : true,
11486     
11487     /**
11488      * @cfg {Boolean} errorMask (true|false) default false
11489      */
11490     errorMask : false,
11491     
11492     /**
11493      * @cfg {Number} maskOffset Default 100
11494      */
11495     maskOffset : 100,
11496     
11497     /**
11498      * @cfg {Boolean} maskBody
11499      */
11500     maskBody : false,
11501
11502     getAutoCreate : function(){
11503
11504         var cfg = {
11505             tag: 'form',
11506             method : this.method || 'POST',
11507             id : this.id || Roo.id(),
11508             cls : ''
11509         };
11510         if (this.parent().xtype.match(/^Nav/)) {
11511             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11512
11513         }
11514
11515         if (this.labelAlign == 'left' ) {
11516             cfg.cls += ' form-horizontal';
11517         }
11518
11519
11520         return cfg;
11521     },
11522     initEvents : function()
11523     {
11524         this.el.on('submit', this.onSubmit, this);
11525         // this was added as random key presses on the form where triggering form submit.
11526         this.el.on('keypress', function(e) {
11527             if (e.getCharCode() != 13) {
11528                 return true;
11529             }
11530             // we might need to allow it for textareas.. and some other items.
11531             // check e.getTarget().
11532
11533             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11534                 return true;
11535             }
11536
11537             Roo.log("keypress blocked");
11538
11539             e.preventDefault();
11540             return false;
11541         });
11542         
11543     },
11544     // private
11545     onSubmit : function(e){
11546         e.stopEvent();
11547     },
11548
11549      /**
11550      * Returns true if client-side validation on the form is successful.
11551      * @return Boolean
11552      */
11553     isValid : function(){
11554         var items = this.getItems();
11555         var valid = true;
11556         var target = false;
11557         
11558         items.each(function(f){
11559             
11560             if(f.validate()){
11561                 return;
11562             }
11563             
11564             Roo.log('invalid field: ' + f.name);
11565             
11566             valid = false;
11567
11568             if(!target && f.el.isVisible(true)){
11569                 target = f;
11570             }
11571            
11572         });
11573         
11574         if(this.errorMask && !valid){
11575             Roo.bootstrap.form.Form.popover.mask(this, target);
11576         }
11577         
11578         return valid;
11579     },
11580     
11581     /**
11582      * Returns true if any fields in this form have changed since their original load.
11583      * @return Boolean
11584      */
11585     isDirty : function(){
11586         var dirty = false;
11587         var items = this.getItems();
11588         items.each(function(f){
11589            if(f.isDirty()){
11590                dirty = true;
11591                return false;
11592            }
11593            return true;
11594         });
11595         return dirty;
11596     },
11597      /**
11598      * Performs a predefined action (submit or load) or custom actions you define on this form.
11599      * @param {String} actionName The name of the action type
11600      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11601      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11602      * accept other config options):
11603      * <pre>
11604 Property          Type             Description
11605 ----------------  ---------------  ----------------------------------------------------------------------------------
11606 url               String           The url for the action (defaults to the form's url)
11607 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11608 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11609 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11610                                    validate the form on the client (defaults to false)
11611      * </pre>
11612      * @return {BasicForm} this
11613      */
11614     doAction : function(action, options){
11615         if(typeof action == 'string'){
11616             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11617         }
11618         if(this.fireEvent('beforeaction', this, action) !== false){
11619             this.beforeAction(action);
11620             action.run.defer(100, action);
11621         }
11622         return this;
11623     },
11624
11625     // private
11626     beforeAction : function(action){
11627         var o = action.options;
11628         
11629         if(this.loadMask){
11630             
11631             if(this.maskBody){
11632                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11633             } else {
11634                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11635             }
11636         }
11637         // not really supported yet.. ??
11638
11639         //if(this.waitMsgTarget === true){
11640         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11641         //}else if(this.waitMsgTarget){
11642         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11643         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11644         //}else {
11645         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11646        // }
11647
11648     },
11649
11650     // private
11651     afterAction : function(action, success){
11652         this.activeAction = null;
11653         var o = action.options;
11654
11655         if(this.loadMask){
11656             
11657             if(this.maskBody){
11658                 Roo.get(document.body).unmask();
11659             } else {
11660                 this.el.unmask();
11661             }
11662         }
11663         
11664         //if(this.waitMsgTarget === true){
11665 //            this.el.unmask();
11666         //}else if(this.waitMsgTarget){
11667         //    this.waitMsgTarget.unmask();
11668         //}else{
11669         //    Roo.MessageBox.updateProgress(1);
11670         //    Roo.MessageBox.hide();
11671        // }
11672         //
11673         if(success){
11674             if(o.reset){
11675                 this.reset();
11676             }
11677             Roo.callback(o.success, o.scope, [this, action]);
11678             this.fireEvent('actioncomplete', this, action);
11679
11680         }else{
11681
11682             // failure condition..
11683             // we have a scenario where updates need confirming.
11684             // eg. if a locking scenario exists..
11685             // we look for { errors : { needs_confirm : true }} in the response.
11686             if (
11687                 (typeof(action.result) != 'undefined')  &&
11688                 (typeof(action.result.errors) != 'undefined')  &&
11689                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11690            ){
11691                 var _t = this;
11692                 Roo.log("not supported yet");
11693                  /*
11694
11695                 Roo.MessageBox.confirm(
11696                     "Change requires confirmation",
11697                     action.result.errorMsg,
11698                     function(r) {
11699                         if (r != 'yes') {
11700                             return;
11701                         }
11702                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11703                     }
11704
11705                 );
11706                 */
11707
11708
11709                 return;
11710             }
11711
11712             Roo.callback(o.failure, o.scope, [this, action]);
11713             // show an error message if no failed handler is set..
11714             if (!this.hasListener('actionfailed')) {
11715                 Roo.log("need to add dialog support");
11716                 /*
11717                 Roo.MessageBox.alert("Error",
11718                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11719                         action.result.errorMsg :
11720                         "Saving Failed, please check your entries or try again"
11721                 );
11722                 */
11723             }
11724
11725             this.fireEvent('actionfailed', this, action);
11726         }
11727
11728     },
11729     /**
11730      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11731      * @param {String} id The value to search for
11732      * @return Field
11733      */
11734     findField : function(id){
11735         var items = this.getItems();
11736         var field = items.get(id);
11737         if(!field){
11738              items.each(function(f){
11739                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11740                     field = f;
11741                     return false;
11742                 }
11743                 return true;
11744             });
11745         }
11746         return field || null;
11747     },
11748      /**
11749      * Mark fields in this form invalid in bulk.
11750      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11751      * @return {BasicForm} this
11752      */
11753     markInvalid : function(errors){
11754         if(errors instanceof Array){
11755             for(var i = 0, len = errors.length; i < len; i++){
11756                 var fieldError = errors[i];
11757                 var f = this.findField(fieldError.id);
11758                 if(f){
11759                     f.markInvalid(fieldError.msg);
11760                 }
11761             }
11762         }else{
11763             var field, id;
11764             for(id in errors){
11765                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11766                     field.markInvalid(errors[id]);
11767                 }
11768             }
11769         }
11770         //Roo.each(this.childForms || [], function (f) {
11771         //    f.markInvalid(errors);
11772         //});
11773
11774         return this;
11775     },
11776
11777     /**
11778      * Set values for fields in this form in bulk.
11779      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11780      * @return {BasicForm} this
11781      */
11782     setValues : function(values){
11783         if(values instanceof Array){ // array of objects
11784             for(var i = 0, len = values.length; i < len; i++){
11785                 var v = values[i];
11786                 var f = this.findField(v.id);
11787                 if(f){
11788                     f.setValue(v.value);
11789                     if(this.trackResetOnLoad){
11790                         f.originalValue = f.getValue();
11791                     }
11792                 }
11793             }
11794         }else{ // object hash
11795             var field, id;
11796             for(id in values){
11797                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11798
11799                     if (field.setFromData &&
11800                         field.valueField &&
11801                         field.displayField &&
11802                         // combos' with local stores can
11803                         // be queried via setValue()
11804                         // to set their value..
11805                         (field.store && !field.store.isLocal)
11806                         ) {
11807                         // it's a combo
11808                         var sd = { };
11809                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11810                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11811                         field.setFromData(sd);
11812
11813                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11814                         
11815                         field.setFromData(values);
11816                         
11817                     } else {
11818                         field.setValue(values[id]);
11819                     }
11820
11821
11822                     if(this.trackResetOnLoad){
11823                         field.originalValue = field.getValue();
11824                     }
11825                 }
11826             }
11827         }
11828
11829         //Roo.each(this.childForms || [], function (f) {
11830         //    f.setValues(values);
11831         //});
11832
11833         return this;
11834     },
11835
11836     /**
11837      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11838      * they are returned as an array.
11839      * @param {Boolean} asString
11840      * @return {Object}
11841      */
11842     getValues : function(asString){
11843         //if (this.childForms) {
11844             // copy values from the child forms
11845         //    Roo.each(this.childForms, function (f) {
11846         //        this.setValues(f.getValues());
11847         //    }, this);
11848         //}
11849
11850
11851
11852         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11853         if(asString === true){
11854             return fs;
11855         }
11856         return Roo.urlDecode(fs);
11857     },
11858
11859     /**
11860      * Returns the fields in this form as an object with key/value pairs.
11861      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11862      * @return {Object}
11863      */
11864     getFieldValues : function(with_hidden)
11865     {
11866         var items = this.getItems();
11867         var ret = {};
11868         items.each(function(f){
11869             
11870             if (!f.getName()) {
11871                 return;
11872             }
11873             
11874             var v = f.getValue();
11875             
11876             if (f.inputType =='radio') {
11877                 if (typeof(ret[f.getName()]) == 'undefined') {
11878                     ret[f.getName()] = ''; // empty..
11879                 }
11880
11881                 if (!f.el.dom.checked) {
11882                     return;
11883
11884                 }
11885                 v = f.el.dom.value;
11886
11887             }
11888             
11889             if(f.xtype == 'MoneyField'){
11890                 ret[f.currencyName] = f.getCurrency();
11891             }
11892
11893             // not sure if this supported any more..
11894             if ((typeof(v) == 'object') && f.getRawValue) {
11895                 v = f.getRawValue() ; // dates..
11896             }
11897             // combo boxes where name != hiddenName...
11898             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11899                 ret[f.name] = f.getRawValue();
11900             }
11901             ret[f.getName()] = v;
11902         });
11903
11904         return ret;
11905     },
11906
11907     /**
11908      * Clears all invalid messages in this form.
11909      * @return {BasicForm} this
11910      */
11911     clearInvalid : function(){
11912         var items = this.getItems();
11913
11914         items.each(function(f){
11915            f.clearInvalid();
11916         });
11917
11918         return this;
11919     },
11920
11921     /**
11922      * Resets this form.
11923      * @return {BasicForm} this
11924      */
11925     reset : function(){
11926         var items = this.getItems();
11927         items.each(function(f){
11928             f.reset();
11929         });
11930
11931         Roo.each(this.childForms || [], function (f) {
11932             f.reset();
11933         });
11934
11935
11936         return this;
11937     },
11938     
11939     getItems : function()
11940     {
11941         var r=new Roo.util.MixedCollection(false, function(o){
11942             return o.id || (o.id = Roo.id());
11943         });
11944         var iter = function(el) {
11945             if (el.inputEl) {
11946                 r.add(el);
11947             }
11948             if (!el.items) {
11949                 return;
11950             }
11951             Roo.each(el.items,function(e) {
11952                 iter(e);
11953             });
11954         };
11955
11956         iter(this);
11957         return r;
11958     },
11959     
11960     hideFields : function(items)
11961     {
11962         Roo.each(items, function(i){
11963             
11964             var f = this.findField(i);
11965             
11966             if(!f){
11967                 return;
11968             }
11969             
11970             f.hide();
11971             
11972         }, this);
11973     },
11974     
11975     showFields : function(items)
11976     {
11977         Roo.each(items, function(i){
11978             
11979             var f = this.findField(i);
11980             
11981             if(!f){
11982                 return;
11983             }
11984             
11985             f.show();
11986             
11987         }, this);
11988     }
11989
11990 });
11991
11992 Roo.apply(Roo.bootstrap.form.Form, {
11993     
11994     popover : {
11995         
11996         padding : 5,
11997         
11998         isApplied : false,
11999         
12000         isMasked : false,
12001         
12002         form : false,
12003         
12004         target : false,
12005         
12006         toolTip : false,
12007         
12008         intervalID : false,
12009         
12010         maskEl : false,
12011         
12012         apply : function()
12013         {
12014             if(this.isApplied){
12015                 return;
12016             }
12017             
12018             this.maskEl = {
12019                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12020                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12021                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12022                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12023             };
12024             
12025             this.maskEl.top.enableDisplayMode("block");
12026             this.maskEl.left.enableDisplayMode("block");
12027             this.maskEl.bottom.enableDisplayMode("block");
12028             this.maskEl.right.enableDisplayMode("block");
12029             
12030             this.toolTip = new Roo.bootstrap.Tooltip({
12031                 cls : 'roo-form-error-popover',
12032                 alignment : {
12033                     'left' : ['r-l', [-2,0], 'right'],
12034                     'right' : ['l-r', [2,0], 'left'],
12035                     'bottom' : ['tl-bl', [0,2], 'top'],
12036                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12037                 }
12038             });
12039             
12040             this.toolTip.render(Roo.get(document.body));
12041
12042             this.toolTip.el.enableDisplayMode("block");
12043             
12044             Roo.get(document.body).on('click', function(){
12045                 this.unmask();
12046             }, this);
12047             
12048             Roo.get(document.body).on('touchstart', function(){
12049                 this.unmask();
12050             }, this);
12051             
12052             this.isApplied = true
12053         },
12054         
12055         mask : function(form, target)
12056         {
12057             this.form = form;
12058             
12059             this.target = target;
12060             
12061             if(!this.form.errorMask || !target.el){
12062                 return;
12063             }
12064             
12065             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12066             
12067             Roo.log(scrollable);
12068             
12069             var ot = this.target.el.calcOffsetsTo(scrollable);
12070             
12071             var scrollTo = ot[1] - this.form.maskOffset;
12072             
12073             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12074             
12075             scrollable.scrollTo('top', scrollTo);
12076             
12077             var box = this.target.el.getBox();
12078             Roo.log(box);
12079             var zIndex = Roo.bootstrap.Modal.zIndex++;
12080
12081             
12082             this.maskEl.top.setStyle('position', 'absolute');
12083             this.maskEl.top.setStyle('z-index', zIndex);
12084             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12085             this.maskEl.top.setLeft(0);
12086             this.maskEl.top.setTop(0);
12087             this.maskEl.top.show();
12088             
12089             this.maskEl.left.setStyle('position', 'absolute');
12090             this.maskEl.left.setStyle('z-index', zIndex);
12091             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12092             this.maskEl.left.setLeft(0);
12093             this.maskEl.left.setTop(box.y - this.padding);
12094             this.maskEl.left.show();
12095
12096             this.maskEl.bottom.setStyle('position', 'absolute');
12097             this.maskEl.bottom.setStyle('z-index', zIndex);
12098             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12099             this.maskEl.bottom.setLeft(0);
12100             this.maskEl.bottom.setTop(box.bottom + this.padding);
12101             this.maskEl.bottom.show();
12102
12103             this.maskEl.right.setStyle('position', 'absolute');
12104             this.maskEl.right.setStyle('z-index', zIndex);
12105             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12106             this.maskEl.right.setLeft(box.right + this.padding);
12107             this.maskEl.right.setTop(box.y - this.padding);
12108             this.maskEl.right.show();
12109
12110             this.toolTip.bindEl = this.target.el;
12111
12112             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12113
12114             var tip = this.target.blankText;
12115
12116             if(this.target.getValue() !== '' ) {
12117                 
12118                 if (this.target.invalidText.length) {
12119                     tip = this.target.invalidText;
12120                 } else if (this.target.regexText.length){
12121                     tip = this.target.regexText;
12122                 }
12123             }
12124
12125             this.toolTip.show(tip);
12126
12127             this.intervalID = window.setInterval(function() {
12128                 Roo.bootstrap.form.Form.popover.unmask();
12129             }, 10000);
12130
12131             window.onwheel = function(){ return false;};
12132             
12133             (function(){ this.isMasked = true; }).defer(500, this);
12134             
12135         },
12136         
12137         unmask : function()
12138         {
12139             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12140                 return;
12141             }
12142             
12143             this.maskEl.top.setStyle('position', 'absolute');
12144             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12145             this.maskEl.top.hide();
12146
12147             this.maskEl.left.setStyle('position', 'absolute');
12148             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12149             this.maskEl.left.hide();
12150
12151             this.maskEl.bottom.setStyle('position', 'absolute');
12152             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12153             this.maskEl.bottom.hide();
12154
12155             this.maskEl.right.setStyle('position', 'absolute');
12156             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12157             this.maskEl.right.hide();
12158             
12159             this.toolTip.hide();
12160             
12161             this.toolTip.el.hide();
12162             
12163             window.onwheel = function(){ return true;};
12164             
12165             if(this.intervalID){
12166                 window.clearInterval(this.intervalID);
12167                 this.intervalID = false;
12168             }
12169             
12170             this.isMasked = false;
12171             
12172         }
12173         
12174     }
12175     
12176 });
12177
12178 /*
12179  * Based on:
12180  * Ext JS Library 1.1.1
12181  * Copyright(c) 2006-2007, Ext JS, LLC.
12182  *
12183  * Originally Released Under LGPL - original licence link has changed is not relivant.
12184  *
12185  * Fork - LGPL
12186  * <script type="text/javascript">
12187  */
12188 /**
12189  * @class Roo.form.VTypes
12190  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12191  * @static
12192  */
12193 Roo.form.VTypes = function(){
12194     // closure these in so they are only created once.
12195     var alpha = /^[a-zA-Z_]+$/;
12196     var alphanum = /^[a-zA-Z0-9_]+$/;
12197     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12198     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12199
12200     // All these messages and functions are configurable
12201     return {
12202         /**
12203          * The function used to validate email addresses
12204          * @param {String} value The email address
12205          */
12206         email : function(v){
12207             return email.test(v);
12208         },
12209         /**
12210          * The error text to display when the email validation function returns false
12211          * @type String
12212          */
12213         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12214         /**
12215          * The keystroke filter mask to be applied on email input
12216          * @type RegExp
12217          */
12218         emailMask : /[a-z0-9_\.\-@]/i,
12219
12220         /**
12221          * The function used to validate URLs
12222          * @param {String} value The URL
12223          */
12224         url : function(v){
12225             return url.test(v);
12226         },
12227         /**
12228          * The error text to display when the url validation function returns false
12229          * @type String
12230          */
12231         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12232         
12233         /**
12234          * The function used to validate alpha values
12235          * @param {String} value The value
12236          */
12237         alpha : function(v){
12238             return alpha.test(v);
12239         },
12240         /**
12241          * The error text to display when the alpha validation function returns false
12242          * @type String
12243          */
12244         alphaText : 'This field should only contain letters and _',
12245         /**
12246          * The keystroke filter mask to be applied on alpha input
12247          * @type RegExp
12248          */
12249         alphaMask : /[a-z_]/i,
12250
12251         /**
12252          * The function used to validate alphanumeric values
12253          * @param {String} value The value
12254          */
12255         alphanum : function(v){
12256             return alphanum.test(v);
12257         },
12258         /**
12259          * The error text to display when the alphanumeric validation function returns false
12260          * @type String
12261          */
12262         alphanumText : 'This field should only contain letters, numbers and _',
12263         /**
12264          * The keystroke filter mask to be applied on alphanumeric input
12265          * @type RegExp
12266          */
12267         alphanumMask : /[a-z0-9_]/i
12268     };
12269 }();/*
12270  * - LGPL
12271  *
12272  * Input
12273  * 
12274  */
12275
12276 /**
12277  * @class Roo.bootstrap.form.Input
12278  * @extends Roo.bootstrap.Component
12279  * Bootstrap Input class
12280  * @cfg {Boolean} disabled is it disabled
12281  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12282  * @cfg {String} name name of the input
12283  * @cfg {string} fieldLabel - the label associated
12284  * @cfg {string} placeholder - placeholder to put in text.
12285  * @cfg {string} before - input group add on before
12286  * @cfg {string} after - input group add on after
12287  * @cfg {string} size - (lg|sm) or leave empty..
12288  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12289  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12290  * @cfg {Number} md colspan out of 12 for computer-sized screens
12291  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12292  * @cfg {string} value default value of the input
12293  * @cfg {Number} labelWidth set the width of label 
12294  * @cfg {Number} labellg set the width of label (1-12)
12295  * @cfg {Number} labelmd set the width of label (1-12)
12296  * @cfg {Number} labelsm set the width of label (1-12)
12297  * @cfg {Number} labelxs set the width of label (1-12)
12298  * @cfg {String} labelAlign (top|left)
12299  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12300  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12301  * @cfg {String} indicatorpos (left|right) default left
12302  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12303  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12304  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12305  * @cfg {Roo.bootstrap.Button} before Button to show before
12306  * @cfg {Roo.bootstrap.Button} afterButton to show before
12307  * @cfg {String} align (left|center|right) Default left
12308  * @cfg {Boolean} forceFeedback (true|false) Default false
12309  * 
12310  * @constructor
12311  * Create a new Input
12312  * @param {Object} config The config object
12313  */
12314
12315 Roo.bootstrap.form.Input = function(config){
12316     
12317     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12318     
12319     this.addEvents({
12320         /**
12321          * @event focus
12322          * Fires when this field receives input focus.
12323          * @param {Roo.form.Field} this
12324          */
12325         focus : true,
12326         /**
12327          * @event blur
12328          * Fires when this field loses input focus.
12329          * @param {Roo.form.Field} this
12330          */
12331         blur : true,
12332         /**
12333          * @event specialkey
12334          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12335          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12336          * @param {Roo.form.Field} this
12337          * @param {Roo.EventObject} e The event object
12338          */
12339         specialkey : true,
12340         /**
12341          * @event change
12342          * Fires just before the field blurs if the field value has changed.
12343          * @param {Roo.form.Field} this
12344          * @param {Mixed} newValue The new value
12345          * @param {Mixed} oldValue The original value
12346          */
12347         change : true,
12348         /**
12349          * @event invalid
12350          * Fires after the field has been marked as invalid.
12351          * @param {Roo.form.Field} this
12352          * @param {String} msg The validation message
12353          */
12354         invalid : true,
12355         /**
12356          * @event valid
12357          * Fires after the field has been validated with no errors.
12358          * @param {Roo.form.Field} this
12359          */
12360         valid : true,
12361          /**
12362          * @event keyup
12363          * Fires after the key up
12364          * @param {Roo.form.Field} this
12365          * @param {Roo.EventObject}  e The event Object
12366          */
12367         keyup : true,
12368         /**
12369          * @event paste
12370          * Fires after the user pastes into input
12371          * @param {Roo.form.Field} this
12372          * @param {Roo.EventObject}  e The event Object
12373          */
12374         paste : true
12375     });
12376 };
12377
12378 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12379      /**
12380      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12381       automatic validation (defaults to "keyup").
12382      */
12383     validationEvent : "keyup",
12384      /**
12385      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12386      */
12387     validateOnBlur : true,
12388     /**
12389      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12390      */
12391     validationDelay : 250,
12392      /**
12393      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12394      */
12395     focusClass : "x-form-focus",  // not needed???
12396     
12397        
12398     /**
12399      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12400      */
12401     invalidClass : "has-warning",
12402     
12403     /**
12404      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12405      */
12406     validClass : "has-success",
12407     
12408     /**
12409      * @cfg {Boolean} hasFeedback (true|false) default true
12410      */
12411     hasFeedback : true,
12412     
12413     /**
12414      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12415      */
12416     invalidFeedbackClass : "glyphicon-warning-sign",
12417     
12418     /**
12419      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12420      */
12421     validFeedbackClass : "glyphicon-ok",
12422     
12423     /**
12424      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12425      */
12426     selectOnFocus : false,
12427     
12428      /**
12429      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12430      */
12431     maskRe : null,
12432        /**
12433      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12434      */
12435     vtype : null,
12436     
12437       /**
12438      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12439      */
12440     disableKeyFilter : false,
12441     
12442        /**
12443      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12444      */
12445     disabled : false,
12446      /**
12447      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12448      */
12449     allowBlank : true,
12450     /**
12451      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12452      */
12453     blankText : "Please complete this mandatory field",
12454     
12455      /**
12456      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12457      */
12458     minLength : 0,
12459     /**
12460      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12461      */
12462     maxLength : Number.MAX_VALUE,
12463     /**
12464      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12465      */
12466     minLengthText : "The minimum length for this field is {0}",
12467     /**
12468      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12469      */
12470     maxLengthText : "The maximum length for this field is {0}",
12471   
12472     
12473     /**
12474      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12475      * If available, this function will be called only after the basic validators all return true, and will be passed the
12476      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12477      */
12478     validator : null,
12479     /**
12480      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12481      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12482      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12483      */
12484     regex : null,
12485     /**
12486      * @cfg {String} regexText -- Depricated - use Invalid Text
12487      */
12488     regexText : "",
12489     
12490     /**
12491      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12492      */
12493     invalidText : "",
12494     
12495     
12496     
12497     autocomplete: false,
12498     
12499     
12500     fieldLabel : '',
12501     inputType : 'text',
12502     
12503     name : false,
12504     placeholder: false,
12505     before : false,
12506     after : false,
12507     size : false,
12508     hasFocus : false,
12509     preventMark: false,
12510     isFormField : true,
12511     value : '',
12512     labelWidth : 2,
12513     labelAlign : false,
12514     readOnly : false,
12515     align : false,
12516     formatedValue : false,
12517     forceFeedback : false,
12518     
12519     indicatorpos : 'left',
12520     
12521     labellg : 0,
12522     labelmd : 0,
12523     labelsm : 0,
12524     labelxs : 0,
12525     
12526     capture : '',
12527     accept : '',
12528     
12529     parentLabelAlign : function()
12530     {
12531         var parent = this;
12532         while (parent.parent()) {
12533             parent = parent.parent();
12534             if (typeof(parent.labelAlign) !='undefined') {
12535                 return parent.labelAlign;
12536             }
12537         }
12538         return 'left';
12539         
12540     },
12541     
12542     getAutoCreate : function()
12543     {
12544         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12545         
12546         var id = Roo.id();
12547         
12548         var cfg = {};
12549         
12550         if(this.inputType != 'hidden'){
12551             cfg.cls = 'form-group' //input-group
12552         }
12553         
12554         var input =  {
12555             tag: 'input',
12556             id : id,
12557             type : this.inputType,
12558             value : this.value,
12559             cls : 'form-control',
12560             placeholder : this.placeholder || '',
12561             autocomplete : this.autocomplete || 'new-password'
12562         };
12563         if (this.inputType == 'file') {
12564             input.style = 'overflow:hidden'; // why not in CSS?
12565         }
12566         
12567         if(this.capture.length){
12568             input.capture = this.capture;
12569         }
12570         
12571         if(this.accept.length){
12572             input.accept = this.accept + "/*";
12573         }
12574         
12575         if(this.align){
12576             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12577         }
12578         
12579         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12580             input.maxLength = this.maxLength;
12581         }
12582         
12583         if (this.disabled) {
12584             input.disabled=true;
12585         }
12586         
12587         if (this.readOnly) {
12588             input.readonly=true;
12589         }
12590         
12591         if (this.name) {
12592             input.name = this.name;
12593         }
12594         
12595         if (this.size) {
12596             input.cls += ' input-' + this.size;
12597         }
12598         
12599         var settings=this;
12600         ['xs','sm','md','lg'].map(function(size){
12601             if (settings[size]) {
12602                 cfg.cls += ' col-' + size + '-' + settings[size];
12603             }
12604         });
12605         
12606         var inputblock = input;
12607         
12608         var feedback = {
12609             tag: 'span',
12610             cls: 'glyphicon form-control-feedback'
12611         };
12612             
12613         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12614             
12615             inputblock = {
12616                 cls : 'has-feedback',
12617                 cn :  [
12618                     input,
12619                     feedback
12620                 ] 
12621             };  
12622         }
12623         
12624         if (this.before || this.after) {
12625             
12626             inputblock = {
12627                 cls : 'input-group',
12628                 cn :  [] 
12629             };
12630             
12631             if (this.before && typeof(this.before) == 'string') {
12632                 
12633                 inputblock.cn.push({
12634                     tag :'span',
12635                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12636                     html : this.before
12637                 });
12638             }
12639             if (this.before && typeof(this.before) == 'object') {
12640                 this.before = Roo.factory(this.before);
12641                 
12642                 inputblock.cn.push({
12643                     tag :'span',
12644                     cls : 'roo-input-before input-group-prepend   input-group-' +
12645                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12646                 });
12647             }
12648             
12649             inputblock.cn.push(input);
12650             
12651             if (this.after && typeof(this.after) == 'string') {
12652                 inputblock.cn.push({
12653                     tag :'span',
12654                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12655                     html : this.after
12656                 });
12657             }
12658             if (this.after && typeof(this.after) == 'object') {
12659                 this.after = Roo.factory(this.after);
12660                 
12661                 inputblock.cn.push({
12662                     tag :'span',
12663                     cls : 'roo-input-after input-group-append  input-group-' +
12664                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12665                 });
12666             }
12667             
12668             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12669                 inputblock.cls += ' has-feedback';
12670                 inputblock.cn.push(feedback);
12671             }
12672         };
12673         var indicator = {
12674             tag : 'i',
12675             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12676             tooltip : 'This field is required'
12677         };
12678         if (this.allowBlank ) {
12679             indicator.style = this.allowBlank ? ' display:none' : '';
12680         }
12681         if (align ==='left' && this.fieldLabel.length) {
12682             
12683             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12684             
12685             cfg.cn = [
12686                 indicator,
12687                 {
12688                     tag: 'label',
12689                     'for' :  id,
12690                     cls : 'control-label col-form-label',
12691                     html : this.fieldLabel
12692
12693                 },
12694                 {
12695                     cls : "", 
12696                     cn: [
12697                         inputblock
12698                     ]
12699                 }
12700             ];
12701             
12702             var labelCfg = cfg.cn[1];
12703             var contentCfg = cfg.cn[2];
12704             
12705             if(this.indicatorpos == 'right'){
12706                 cfg.cn = [
12707                     {
12708                         tag: 'label',
12709                         'for' :  id,
12710                         cls : 'control-label col-form-label',
12711                         cn : [
12712                             {
12713                                 tag : 'span',
12714                                 html : this.fieldLabel
12715                             },
12716                             indicator
12717                         ]
12718                     },
12719                     {
12720                         cls : "",
12721                         cn: [
12722                             inputblock
12723                         ]
12724                     }
12725
12726                 ];
12727                 
12728                 labelCfg = cfg.cn[0];
12729                 contentCfg = cfg.cn[1];
12730             
12731             }
12732             
12733             if(this.labelWidth > 12){
12734                 labelCfg.style = "width: " + this.labelWidth + 'px';
12735             }
12736             
12737             if(this.labelWidth < 13 && this.labelmd == 0){
12738                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12739             }
12740             
12741             if(this.labellg > 0){
12742                 labelCfg.cls += ' col-lg-' + this.labellg;
12743                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12744             }
12745             
12746             if(this.labelmd > 0){
12747                 labelCfg.cls += ' col-md-' + this.labelmd;
12748                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12749             }
12750             
12751             if(this.labelsm > 0){
12752                 labelCfg.cls += ' col-sm-' + this.labelsm;
12753                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12754             }
12755             
12756             if(this.labelxs > 0){
12757                 labelCfg.cls += ' col-xs-' + this.labelxs;
12758                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12759             }
12760             
12761             
12762         } else if ( this.fieldLabel.length) {
12763                 
12764             
12765             
12766             cfg.cn = [
12767                 {
12768                     tag : 'i',
12769                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12770                     tooltip : 'This field is required',
12771                     style : this.allowBlank ? ' display:none' : '' 
12772                 },
12773                 {
12774                     tag: 'label',
12775                    //cls : 'input-group-addon',
12776                     html : this.fieldLabel
12777
12778                 },
12779
12780                inputblock
12781
12782            ];
12783            
12784            if(this.indicatorpos == 'right'){
12785        
12786                 cfg.cn = [
12787                     {
12788                         tag: 'label',
12789                        //cls : 'input-group-addon',
12790                         html : this.fieldLabel
12791
12792                     },
12793                     {
12794                         tag : 'i',
12795                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12796                         tooltip : 'This field is required',
12797                         style : this.allowBlank ? ' display:none' : '' 
12798                     },
12799
12800                    inputblock
12801
12802                ];
12803
12804             }
12805
12806         } else {
12807             
12808             cfg.cn = [
12809
12810                     inputblock
12811
12812             ];
12813                 
12814                 
12815         };
12816         
12817         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12818            cfg.cls += ' navbar-form';
12819         }
12820         
12821         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12822             // on BS4 we do this only if not form 
12823             cfg.cls += ' navbar-form';
12824             cfg.tag = 'li';
12825         }
12826         
12827         return cfg;
12828         
12829     },
12830     /**
12831      * return the real input element.
12832      */
12833     inputEl: function ()
12834     {
12835         return this.el.select('input.form-control',true).first();
12836     },
12837     
12838     tooltipEl : function()
12839     {
12840         return this.inputEl();
12841     },
12842     
12843     indicatorEl : function()
12844     {
12845         if (Roo.bootstrap.version == 4) {
12846             return false; // not enabled in v4 yet.
12847         }
12848         
12849         var indicator = this.el.select('i.roo-required-indicator',true).first();
12850         
12851         if(!indicator){
12852             return false;
12853         }
12854         
12855         return indicator;
12856         
12857     },
12858     
12859     setDisabled : function(v)
12860     {
12861         var i  = this.inputEl().dom;
12862         if (!v) {
12863             i.removeAttribute('disabled');
12864             return;
12865             
12866         }
12867         i.setAttribute('disabled','true');
12868     },
12869     initEvents : function()
12870     {
12871           
12872         this.inputEl().on("keydown" , this.fireKey,  this);
12873         this.inputEl().on("focus", this.onFocus,  this);
12874         this.inputEl().on("blur", this.onBlur,  this);
12875         
12876         this.inputEl().relayEvent('keyup', this);
12877         this.inputEl().relayEvent('paste', this);
12878         
12879         this.indicator = this.indicatorEl();
12880         
12881         if(this.indicator){
12882             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12883         }
12884  
12885         // reference to original value for reset
12886         this.originalValue = this.getValue();
12887         //Roo.form.TextField.superclass.initEvents.call(this);
12888         if(this.validationEvent == 'keyup'){
12889             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12890             this.inputEl().on('keyup', this.filterValidation, this);
12891         }
12892         else if(this.validationEvent !== false){
12893             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12894         }
12895         
12896         if(this.selectOnFocus){
12897             this.on("focus", this.preFocus, this);
12898             
12899         }
12900         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12901             this.inputEl().on("keypress", this.filterKeys, this);
12902         } else {
12903             this.inputEl().relayEvent('keypress', this);
12904         }
12905        /* if(this.grow){
12906             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12907             this.el.on("click", this.autoSize,  this);
12908         }
12909         */
12910         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12911             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12912         }
12913         
12914         if (typeof(this.before) == 'object') {
12915             this.before.render(this.el.select('.roo-input-before',true).first());
12916         }
12917         if (typeof(this.after) == 'object') {
12918             this.after.render(this.el.select('.roo-input-after',true).first());
12919         }
12920         
12921         this.inputEl().on('change', this.onChange, this);
12922         
12923     },
12924     filterValidation : function(e){
12925         if(!e.isNavKeyPress()){
12926             this.validationTask.delay(this.validationDelay);
12927         }
12928     },
12929      /**
12930      * Validates the field value
12931      * @return {Boolean} True if the value is valid, else false
12932      */
12933     validate : function(){
12934         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12935         if(this.disabled || this.validateValue(this.getRawValue())){
12936             this.markValid();
12937             return true;
12938         }
12939         
12940         this.markInvalid();
12941         return false;
12942     },
12943     
12944     
12945     /**
12946      * Validates a value according to the field's validation rules and marks the field as invalid
12947      * if the validation fails
12948      * @param {Mixed} value The value to validate
12949      * @return {Boolean} True if the value is valid, else false
12950      */
12951     validateValue : function(value)
12952     {
12953         if(this.getVisibilityEl().hasClass('hidden')){
12954             return true;
12955         }
12956         
12957         if(value.length < 1)  { // if it's blank
12958             if(this.allowBlank){
12959                 return true;
12960             }
12961             return false;
12962         }
12963         
12964         if(value.length < this.minLength){
12965             return false;
12966         }
12967         if(value.length > this.maxLength){
12968             return false;
12969         }
12970         if(this.vtype){
12971             var vt = Roo.form.VTypes;
12972             if(!vt[this.vtype](value, this)){
12973                 return false;
12974             }
12975         }
12976         if(typeof this.validator == "function"){
12977             var msg = this.validator(value);
12978             if (typeof(msg) == 'string') {
12979                 this.invalidText = msg;
12980             }
12981             if(msg !== true){
12982                 return false;
12983             }
12984         }
12985         
12986         if(this.regex && !this.regex.test(value)){
12987             return false;
12988         }
12989         
12990         return true;
12991     },
12992     
12993      // private
12994     fireKey : function(e){
12995         //Roo.log('field ' + e.getKey());
12996         if(e.isNavKeyPress()){
12997             this.fireEvent("specialkey", this, e);
12998         }
12999     },
13000     focus : function (selectText){
13001         if(this.rendered){
13002             this.inputEl().focus();
13003             if(selectText === true){
13004                 this.inputEl().dom.select();
13005             }
13006         }
13007         return this;
13008     } ,
13009     
13010     onFocus : function(){
13011         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13012            // this.el.addClass(this.focusClass);
13013         }
13014         if(!this.hasFocus){
13015             this.hasFocus = true;
13016             this.startValue = this.getValue();
13017             this.fireEvent("focus", this);
13018         }
13019     },
13020     
13021     beforeBlur : Roo.emptyFn,
13022
13023     
13024     // private
13025     onBlur : function(){
13026         this.beforeBlur();
13027         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13028             //this.el.removeClass(this.focusClass);
13029         }
13030         this.hasFocus = false;
13031         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13032             this.validate();
13033         }
13034         var v = this.getValue();
13035         if(String(v) !== String(this.startValue)){
13036             this.fireEvent('change', this, v, this.startValue);
13037         }
13038         this.fireEvent("blur", this);
13039     },
13040     
13041     onChange : function(e)
13042     {
13043         var v = this.getValue();
13044         if(String(v) !== String(this.startValue)){
13045             this.fireEvent('change', this, v, this.startValue);
13046         }
13047         
13048     },
13049     
13050     /**
13051      * Resets the current field value to the originally loaded value and clears any validation messages
13052      */
13053     reset : function(){
13054         this.setValue(this.originalValue);
13055         this.validate();
13056     },
13057      /**
13058      * Returns the name of the field
13059      * @return {Mixed} name The name field
13060      */
13061     getName: function(){
13062         return this.name;
13063     },
13064      /**
13065      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13066      * @return {Mixed} value The field value
13067      */
13068     getValue : function(){
13069         
13070         var v = this.inputEl().getValue();
13071         
13072         return v;
13073     },
13074     /**
13075      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13076      * @return {Mixed} value The field value
13077      */
13078     getRawValue : function(){
13079         var v = this.inputEl().getValue();
13080         
13081         return v;
13082     },
13083     
13084     /**
13085      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13086      * @param {Mixed} value The value to set
13087      */
13088     setRawValue : function(v){
13089         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13090     },
13091     
13092     selectText : function(start, end){
13093         var v = this.getRawValue();
13094         if(v.length > 0){
13095             start = start === undefined ? 0 : start;
13096             end = end === undefined ? v.length : end;
13097             var d = this.inputEl().dom;
13098             if(d.setSelectionRange){
13099                 d.setSelectionRange(start, end);
13100             }else if(d.createTextRange){
13101                 var range = d.createTextRange();
13102                 range.moveStart("character", start);
13103                 range.moveEnd("character", v.length-end);
13104                 range.select();
13105             }
13106         }
13107     },
13108     
13109     /**
13110      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13111      * @param {Mixed} value The value to set
13112      */
13113     setValue : function(v){
13114         this.value = v;
13115         if(this.rendered){
13116             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13117             this.validate();
13118         }
13119     },
13120     
13121     /*
13122     processValue : function(value){
13123         if(this.stripCharsRe){
13124             var newValue = value.replace(this.stripCharsRe, '');
13125             if(newValue !== value){
13126                 this.setRawValue(newValue);
13127                 return newValue;
13128             }
13129         }
13130         return value;
13131     },
13132   */
13133     preFocus : function(){
13134         
13135         if(this.selectOnFocus){
13136             this.inputEl().dom.select();
13137         }
13138     },
13139     filterKeys : function(e){
13140         var k = e.getKey();
13141         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13142             return;
13143         }
13144         var c = e.getCharCode(), cc = String.fromCharCode(c);
13145         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13146             return;
13147         }
13148         if(!this.maskRe.test(cc)){
13149             e.stopEvent();
13150         }
13151     },
13152      /**
13153      * Clear any invalid styles/messages for this field
13154      */
13155     clearInvalid : function(){
13156         
13157         if(!this.el || this.preventMark){ // not rendered
13158             return;
13159         }
13160         
13161         
13162         this.el.removeClass([this.invalidClass, 'is-invalid']);
13163         
13164         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13165             
13166             var feedback = this.el.select('.form-control-feedback', true).first();
13167             
13168             if(feedback){
13169                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13170             }
13171             
13172         }
13173         
13174         if(this.indicator){
13175             this.indicator.removeClass('visible');
13176             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13177         }
13178         
13179         this.fireEvent('valid', this);
13180     },
13181     
13182      /**
13183      * Mark this field as valid
13184      */
13185     markValid : function()
13186     {
13187         if(!this.el  || this.preventMark){ // not rendered...
13188             return;
13189         }
13190         
13191         this.el.removeClass([this.invalidClass, this.validClass]);
13192         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13193
13194         var feedback = this.el.select('.form-control-feedback', true).first();
13195             
13196         if(feedback){
13197             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13198         }
13199         
13200         if(this.indicator){
13201             this.indicator.removeClass('visible');
13202             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13203         }
13204         
13205         if(this.disabled){
13206             return;
13207         }
13208         
13209            
13210         if(this.allowBlank && !this.getRawValue().length){
13211             return;
13212         }
13213         if (Roo.bootstrap.version == 3) {
13214             this.el.addClass(this.validClass);
13215         } else {
13216             this.inputEl().addClass('is-valid');
13217         }
13218
13219         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13220             
13221             var feedback = this.el.select('.form-control-feedback', true).first();
13222             
13223             if(feedback){
13224                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13225                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13226             }
13227             
13228         }
13229         
13230         this.fireEvent('valid', this);
13231     },
13232     
13233      /**
13234      * Mark this field as invalid
13235      * @param {String} msg The validation message
13236      */
13237     markInvalid : function(msg)
13238     {
13239         if(!this.el  || this.preventMark){ // not rendered
13240             return;
13241         }
13242         
13243         this.el.removeClass([this.invalidClass, this.validClass]);
13244         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13245         
13246         var feedback = this.el.select('.form-control-feedback', true).first();
13247             
13248         if(feedback){
13249             this.el.select('.form-control-feedback', true).first().removeClass(
13250                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13251         }
13252
13253         if(this.disabled){
13254             return;
13255         }
13256         
13257         if(this.allowBlank && !this.getRawValue().length){
13258             return;
13259         }
13260         
13261         if(this.indicator){
13262             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13263             this.indicator.addClass('visible');
13264         }
13265         if (Roo.bootstrap.version == 3) {
13266             this.el.addClass(this.invalidClass);
13267         } else {
13268             this.inputEl().addClass('is-invalid');
13269         }
13270         
13271         
13272         
13273         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13274             
13275             var feedback = this.el.select('.form-control-feedback', true).first();
13276             
13277             if(feedback){
13278                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13279                 
13280                 if(this.getValue().length || this.forceFeedback){
13281                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13282                 }
13283                 
13284             }
13285             
13286         }
13287         
13288         this.fireEvent('invalid', this, msg);
13289     },
13290     // private
13291     SafariOnKeyDown : function(event)
13292     {
13293         // this is a workaround for a password hang bug on chrome/ webkit.
13294         if (this.inputEl().dom.type != 'password') {
13295             return;
13296         }
13297         
13298         var isSelectAll = false;
13299         
13300         if(this.inputEl().dom.selectionEnd > 0){
13301             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13302         }
13303         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13304             event.preventDefault();
13305             this.setValue('');
13306             return;
13307         }
13308         
13309         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13310             
13311             event.preventDefault();
13312             // this is very hacky as keydown always get's upper case.
13313             //
13314             var cc = String.fromCharCode(event.getCharCode());
13315             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13316             
13317         }
13318     },
13319     adjustWidth : function(tag, w){
13320         tag = tag.toLowerCase();
13321         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13322             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13323                 if(tag == 'input'){
13324                     return w + 2;
13325                 }
13326                 if(tag == 'textarea'){
13327                     return w-2;
13328                 }
13329             }else if(Roo.isOpera){
13330                 if(tag == 'input'){
13331                     return w + 2;
13332                 }
13333                 if(tag == 'textarea'){
13334                     return w-2;
13335                 }
13336             }
13337         }
13338         return w;
13339     },
13340     
13341     setFieldLabel : function(v)
13342     {
13343         if(!this.rendered){
13344             return;
13345         }
13346         
13347         if(this.indicatorEl()){
13348             var ar = this.el.select('label > span',true);
13349             
13350             if (ar.elements.length) {
13351                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13352                 this.fieldLabel = v;
13353                 return;
13354             }
13355             
13356             var br = this.el.select('label',true);
13357             
13358             if(br.elements.length) {
13359                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13360                 this.fieldLabel = v;
13361                 return;
13362             }
13363             
13364             Roo.log('Cannot Found any of label > span || label in input');
13365             return;
13366         }
13367         
13368         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13369         this.fieldLabel = v;
13370         
13371         
13372     }
13373 });
13374
13375  
13376 /*
13377  * - LGPL
13378  *
13379  * Input
13380  * 
13381  */
13382
13383 /**
13384  * @class Roo.bootstrap.form.TextArea
13385  * @extends Roo.bootstrap.form.Input
13386  * Bootstrap TextArea class
13387  * @cfg {Number} cols Specifies the visible width of a text area
13388  * @cfg {Number} rows Specifies the visible number of lines in a text area
13389  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13390  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13391  * @cfg {string} html text
13392  * 
13393  * @constructor
13394  * Create a new TextArea
13395  * @param {Object} config The config object
13396  */
13397
13398 Roo.bootstrap.form.TextArea = function(config){
13399     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13400    
13401 };
13402
13403 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13404      
13405     cols : false,
13406     rows : 5,
13407     readOnly : false,
13408     warp : 'soft',
13409     resize : false,
13410     value: false,
13411     html: false,
13412     
13413     getAutoCreate : function(){
13414         
13415         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13416         
13417         var id = Roo.id();
13418         
13419         var cfg = {};
13420         
13421         if(this.inputType != 'hidden'){
13422             cfg.cls = 'form-group' //input-group
13423         }
13424         
13425         var input =  {
13426             tag: 'textarea',
13427             id : id,
13428             warp : this.warp,
13429             rows : this.rows,
13430             value : this.value || '',
13431             html: this.html || '',
13432             cls : 'form-control',
13433             placeholder : this.placeholder || '' 
13434             
13435         };
13436         
13437         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13438             input.maxLength = this.maxLength;
13439         }
13440         
13441         if(this.resize){
13442             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13443         }
13444         
13445         if(this.cols){
13446             input.cols = this.cols;
13447         }
13448         
13449         if (this.readOnly) {
13450             input.readonly = true;
13451         }
13452         
13453         if (this.name) {
13454             input.name = this.name;
13455         }
13456         
13457         if (this.size) {
13458             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13459         }
13460         
13461         var settings=this;
13462         ['xs','sm','md','lg'].map(function(size){
13463             if (settings[size]) {
13464                 cfg.cls += ' col-' + size + '-' + settings[size];
13465             }
13466         });
13467         
13468         var inputblock = input;
13469         
13470         if(this.hasFeedback && !this.allowBlank){
13471             
13472             var feedback = {
13473                 tag: 'span',
13474                 cls: 'glyphicon form-control-feedback'
13475             };
13476
13477             inputblock = {
13478                 cls : 'has-feedback',
13479                 cn :  [
13480                     input,
13481                     feedback
13482                 ] 
13483             };  
13484         }
13485         
13486         
13487         if (this.before || this.after) {
13488             
13489             inputblock = {
13490                 cls : 'input-group',
13491                 cn :  [] 
13492             };
13493             if (this.before) {
13494                 inputblock.cn.push({
13495                     tag :'span',
13496                     cls : 'input-group-addon',
13497                     html : this.before
13498                 });
13499             }
13500             
13501             inputblock.cn.push(input);
13502             
13503             if(this.hasFeedback && !this.allowBlank){
13504                 inputblock.cls += ' has-feedback';
13505                 inputblock.cn.push(feedback);
13506             }
13507             
13508             if (this.after) {
13509                 inputblock.cn.push({
13510                     tag :'span',
13511                     cls : 'input-group-addon',
13512                     html : this.after
13513                 });
13514             }
13515             
13516         }
13517         
13518         if (align ==='left' && this.fieldLabel.length) {
13519             cfg.cn = [
13520                 {
13521                     tag: 'label',
13522                     'for' :  id,
13523                     cls : 'control-label',
13524                     html : this.fieldLabel
13525                 },
13526                 {
13527                     cls : "",
13528                     cn: [
13529                         inputblock
13530                     ]
13531                 }
13532
13533             ];
13534             
13535             if(this.labelWidth > 12){
13536                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13537             }
13538
13539             if(this.labelWidth < 13 && this.labelmd == 0){
13540                 this.labelmd = this.labelWidth;
13541             }
13542
13543             if(this.labellg > 0){
13544                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13545                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13546             }
13547
13548             if(this.labelmd > 0){
13549                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13550                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13551             }
13552
13553             if(this.labelsm > 0){
13554                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13555                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13556             }
13557
13558             if(this.labelxs > 0){
13559                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13560                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13561             }
13562             
13563         } else if ( this.fieldLabel.length) {
13564             cfg.cn = [
13565
13566                {
13567                    tag: 'label',
13568                    //cls : 'input-group-addon',
13569                    html : this.fieldLabel
13570
13571                },
13572
13573                inputblock
13574
13575            ];
13576
13577         } else {
13578
13579             cfg.cn = [
13580
13581                 inputblock
13582
13583             ];
13584                 
13585         }
13586         
13587         if (this.disabled) {
13588             input.disabled=true;
13589         }
13590         
13591         return cfg;
13592         
13593     },
13594     /**
13595      * return the real textarea element.
13596      */
13597     inputEl: function ()
13598     {
13599         return this.el.select('textarea.form-control',true).first();
13600     },
13601     
13602     /**
13603      * Clear any invalid styles/messages for this field
13604      */
13605     clearInvalid : function()
13606     {
13607         
13608         if(!this.el || this.preventMark){ // not rendered
13609             return;
13610         }
13611         
13612         var label = this.el.select('label', true).first();
13613         var icon = this.el.select('i.fa-star', true).first();
13614         
13615         if(label && icon){
13616             icon.remove();
13617         }
13618         this.el.removeClass( this.validClass);
13619         this.inputEl().removeClass('is-invalid');
13620          
13621         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13622             
13623             var feedback = this.el.select('.form-control-feedback', true).first();
13624             
13625             if(feedback){
13626                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13627             }
13628             
13629         }
13630         
13631         this.fireEvent('valid', this);
13632     },
13633     
13634      /**
13635      * Mark this field as valid
13636      */
13637     markValid : function()
13638     {
13639         if(!this.el  || this.preventMark){ // not rendered
13640             return;
13641         }
13642         
13643         this.el.removeClass([this.invalidClass, this.validClass]);
13644         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13645         
13646         var feedback = this.el.select('.form-control-feedback', true).first();
13647             
13648         if(feedback){
13649             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13650         }
13651
13652         if(this.disabled || this.allowBlank){
13653             return;
13654         }
13655         
13656         var label = this.el.select('label', true).first();
13657         var icon = this.el.select('i.fa-star', true).first();
13658         
13659         if(label && icon){
13660             icon.remove();
13661         }
13662         if (Roo.bootstrap.version == 3) {
13663             this.el.addClass(this.validClass);
13664         } else {
13665             this.inputEl().addClass('is-valid');
13666         }
13667         
13668         
13669         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13670             
13671             var feedback = this.el.select('.form-control-feedback', true).first();
13672             
13673             if(feedback){
13674                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13675                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13676             }
13677             
13678         }
13679         
13680         this.fireEvent('valid', this);
13681     },
13682     
13683      /**
13684      * Mark this field as invalid
13685      * @param {String} msg The validation message
13686      */
13687     markInvalid : function(msg)
13688     {
13689         if(!this.el  || this.preventMark){ // not rendered
13690             return;
13691         }
13692         
13693         this.el.removeClass([this.invalidClass, this.validClass]);
13694         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13695         
13696         var feedback = this.el.select('.form-control-feedback', true).first();
13697             
13698         if(feedback){
13699             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13700         }
13701
13702         if(this.disabled || this.allowBlank){
13703             return;
13704         }
13705         
13706         var label = this.el.select('label', true).first();
13707         var icon = this.el.select('i.fa-star', true).first();
13708         
13709         if(!this.getValue().length && label && !icon){
13710             this.el.createChild({
13711                 tag : 'i',
13712                 cls : 'text-danger fa fa-lg fa-star',
13713                 tooltip : 'This field is required',
13714                 style : 'margin-right:5px;'
13715             }, label, true);
13716         }
13717         
13718         if (Roo.bootstrap.version == 3) {
13719             this.el.addClass(this.invalidClass);
13720         } else {
13721             this.inputEl().addClass('is-invalid');
13722         }
13723         
13724         // fixme ... this may be depricated need to test..
13725         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13726             
13727             var feedback = this.el.select('.form-control-feedback', true).first();
13728             
13729             if(feedback){
13730                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13731                 
13732                 if(this.getValue().length || this.forceFeedback){
13733                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13734                 }
13735                 
13736             }
13737             
13738         }
13739         
13740         this.fireEvent('invalid', this, msg);
13741     }
13742 });
13743
13744  
13745 /*
13746  * - LGPL
13747  *
13748  * trigger field - base class for combo..
13749  * 
13750  */
13751  
13752 /**
13753  * @class Roo.bootstrap.form.TriggerField
13754  * @extends Roo.bootstrap.form.Input
13755  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13756  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13757  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13758  * for which you can provide a custom implementation.  For example:
13759  * <pre><code>
13760 var trigger = new Roo.bootstrap.form.TriggerField();
13761 trigger.onTriggerClick = myTriggerFn;
13762 trigger.applyTo('my-field');
13763 </code></pre>
13764  *
13765  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13766  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13767  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13768  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13769  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13770
13771  * @constructor
13772  * Create a new TriggerField.
13773  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13774  * to the base TextField)
13775  */
13776 Roo.bootstrap.form.TriggerField = function(config){
13777     this.mimicing = false;
13778     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13779 };
13780
13781 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13782     /**
13783      * @cfg {String} triggerClass A CSS class to apply to the trigger
13784      */
13785      /**
13786      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13787      */
13788     hideTrigger:false,
13789
13790     /**
13791      * @cfg {Boolean} removable (true|false) special filter default false
13792      */
13793     removable : false,
13794     
13795     /** @cfg {Boolean} grow @hide */
13796     /** @cfg {Number} growMin @hide */
13797     /** @cfg {Number} growMax @hide */
13798
13799     /**
13800      * @hide 
13801      * @method
13802      */
13803     autoSize: Roo.emptyFn,
13804     // private
13805     monitorTab : true,
13806     // private
13807     deferHeight : true,
13808
13809     
13810     actionMode : 'wrap',
13811     
13812     caret : false,
13813     
13814     
13815     getAutoCreate : function(){
13816        
13817         var align = this.labelAlign || this.parentLabelAlign();
13818         
13819         var id = Roo.id();
13820         
13821         var cfg = {
13822             cls: 'form-group' //input-group
13823         };
13824         
13825         
13826         var input =  {
13827             tag: 'input',
13828             id : id,
13829             type : this.inputType,
13830             cls : 'form-control',
13831             autocomplete: 'new-password',
13832             placeholder : this.placeholder || '' 
13833             
13834         };
13835         if (this.name) {
13836             input.name = this.name;
13837         }
13838         if (this.size) {
13839             input.cls += ' input-' + this.size;
13840         }
13841         
13842         if (this.disabled) {
13843             input.disabled=true;
13844         }
13845         
13846         var inputblock = input;
13847         
13848         if(this.hasFeedback && !this.allowBlank){
13849             
13850             var feedback = {
13851                 tag: 'span',
13852                 cls: 'glyphicon form-control-feedback'
13853             };
13854             
13855             if(this.removable && !this.editable  ){
13856                 inputblock = {
13857                     cls : 'has-feedback',
13858                     cn :  [
13859                         inputblock,
13860                         {
13861                             tag: 'button',
13862                             html : 'x',
13863                             cls : 'roo-combo-removable-btn close'
13864                         },
13865                         feedback
13866                     ] 
13867                 };
13868             } else {
13869                 inputblock = {
13870                     cls : 'has-feedback',
13871                     cn :  [
13872                         inputblock,
13873                         feedback
13874                     ] 
13875                 };
13876             }
13877
13878         } else {
13879             if(this.removable && !this.editable ){
13880                 inputblock = {
13881                     cls : 'roo-removable',
13882                     cn :  [
13883                         inputblock,
13884                         {
13885                             tag: 'button',
13886                             html : 'x',
13887                             cls : 'roo-combo-removable-btn close'
13888                         }
13889                     ] 
13890                 };
13891             }
13892         }
13893         
13894         if (this.before || this.after) {
13895             
13896             inputblock = {
13897                 cls : 'input-group',
13898                 cn :  [] 
13899             };
13900             if (this.before) {
13901                 inputblock.cn.push({
13902                     tag :'span',
13903                     cls : 'input-group-addon input-group-prepend input-group-text',
13904                     html : this.before
13905                 });
13906             }
13907             
13908             inputblock.cn.push(input);
13909             
13910             if(this.hasFeedback && !this.allowBlank){
13911                 inputblock.cls += ' has-feedback';
13912                 inputblock.cn.push(feedback);
13913             }
13914             
13915             if (this.after) {
13916                 inputblock.cn.push({
13917                     tag :'span',
13918                     cls : 'input-group-addon input-group-append input-group-text',
13919                     html : this.after
13920                 });
13921             }
13922             
13923         };
13924         
13925       
13926         
13927         var ibwrap = inputblock;
13928         
13929         if(this.multiple){
13930             ibwrap = {
13931                 tag: 'ul',
13932                 cls: 'roo-select2-choices',
13933                 cn:[
13934                     {
13935                         tag: 'li',
13936                         cls: 'roo-select2-search-field',
13937                         cn: [
13938
13939                             inputblock
13940                         ]
13941                     }
13942                 ]
13943             };
13944                 
13945         }
13946         
13947         var combobox = {
13948             cls: 'roo-select2-container input-group',
13949             cn: [
13950                  {
13951                     tag: 'input',
13952                     type : 'hidden',
13953                     cls: 'form-hidden-field'
13954                 },
13955                 ibwrap
13956             ]
13957         };
13958         
13959         if(!this.multiple && this.showToggleBtn){
13960             
13961             var caret = {
13962                         tag: 'span',
13963                         cls: 'caret'
13964              };
13965             if (this.caret != false) {
13966                 caret = {
13967                      tag: 'i',
13968                      cls: 'fa fa-' + this.caret
13969                 };
13970                 
13971             }
13972             
13973             combobox.cn.push({
13974                 tag :'span',
13975                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13976                 cn : [
13977                     Roo.bootstrap.version == 3 ? caret : '',
13978                     {
13979                         tag: 'span',
13980                         cls: 'combobox-clear',
13981                         cn  : [
13982                             {
13983                                 tag : 'i',
13984                                 cls: 'icon-remove'
13985                             }
13986                         ]
13987                     }
13988                 ]
13989
13990             })
13991         }
13992         
13993         if(this.multiple){
13994             combobox.cls += ' roo-select2-container-multi';
13995         }
13996          var indicator = {
13997             tag : 'i',
13998             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13999             tooltip : 'This field is required'
14000         };
14001         if (Roo.bootstrap.version == 4) {
14002             indicator = {
14003                 tag : 'i',
14004                 style : 'display:none'
14005             };
14006         }
14007         
14008         
14009         if (align ==='left' && this.fieldLabel.length) {
14010             
14011             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14012
14013             cfg.cn = [
14014                 indicator,
14015                 {
14016                     tag: 'label',
14017                     'for' :  id,
14018                     cls : 'control-label',
14019                     html : this.fieldLabel
14020
14021                 },
14022                 {
14023                     cls : "", 
14024                     cn: [
14025                         combobox
14026                     ]
14027                 }
14028
14029             ];
14030             
14031             var labelCfg = cfg.cn[1];
14032             var contentCfg = cfg.cn[2];
14033             
14034             if(this.indicatorpos == 'right'){
14035                 cfg.cn = [
14036                     {
14037                         tag: 'label',
14038                         'for' :  id,
14039                         cls : 'control-label',
14040                         cn : [
14041                             {
14042                                 tag : 'span',
14043                                 html : this.fieldLabel
14044                             },
14045                             indicator
14046                         ]
14047                     },
14048                     {
14049                         cls : "", 
14050                         cn: [
14051                             combobox
14052                         ]
14053                     }
14054
14055                 ];
14056                 
14057                 labelCfg = cfg.cn[0];
14058                 contentCfg = cfg.cn[1];
14059             }
14060             
14061             if(this.labelWidth > 12){
14062                 labelCfg.style = "width: " + this.labelWidth + 'px';
14063             }
14064             
14065             if(this.labelWidth < 13 && this.labelmd == 0){
14066                 this.labelmd = this.labelWidth;
14067             }
14068             
14069             if(this.labellg > 0){
14070                 labelCfg.cls += ' col-lg-' + this.labellg;
14071                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14072             }
14073             
14074             if(this.labelmd > 0){
14075                 labelCfg.cls += ' col-md-' + this.labelmd;
14076                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14077             }
14078             
14079             if(this.labelsm > 0){
14080                 labelCfg.cls += ' col-sm-' + this.labelsm;
14081                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14082             }
14083             
14084             if(this.labelxs > 0){
14085                 labelCfg.cls += ' col-xs-' + this.labelxs;
14086                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14087             }
14088             
14089         } else if ( this.fieldLabel.length) {
14090 //                Roo.log(" label");
14091             cfg.cn = [
14092                 indicator,
14093                {
14094                    tag: 'label',
14095                    //cls : 'input-group-addon',
14096                    html : this.fieldLabel
14097
14098                },
14099
14100                combobox
14101
14102             ];
14103             
14104             if(this.indicatorpos == 'right'){
14105                 
14106                 cfg.cn = [
14107                     {
14108                        tag: 'label',
14109                        cn : [
14110                            {
14111                                tag : 'span',
14112                                html : this.fieldLabel
14113                            },
14114                            indicator
14115                        ]
14116
14117                     },
14118                     combobox
14119
14120                 ];
14121
14122             }
14123
14124         } else {
14125             
14126 //                Roo.log(" no label && no align");
14127                 cfg = combobox
14128                      
14129                 
14130         }
14131         
14132         var settings=this;
14133         ['xs','sm','md','lg'].map(function(size){
14134             if (settings[size]) {
14135                 cfg.cls += ' col-' + size + '-' + settings[size];
14136             }
14137         });
14138         
14139         return cfg;
14140         
14141     },
14142     
14143     
14144     
14145     // private
14146     onResize : function(w, h){
14147 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14148 //        if(typeof w == 'number'){
14149 //            var x = w - this.trigger.getWidth();
14150 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14151 //            this.trigger.setStyle('left', x+'px');
14152 //        }
14153     },
14154
14155     // private
14156     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14157
14158     // private
14159     getResizeEl : function(){
14160         return this.inputEl();
14161     },
14162
14163     // private
14164     getPositionEl : function(){
14165         return this.inputEl();
14166     },
14167
14168     // private
14169     alignErrorIcon : function(){
14170         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14171     },
14172
14173     // private
14174     initEvents : function(){
14175         
14176         this.createList();
14177         
14178         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14179         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14180         if(!this.multiple && this.showToggleBtn){
14181             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14182             if(this.hideTrigger){
14183                 this.trigger.setDisplayed(false);
14184             }
14185             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14186         }
14187         
14188         if(this.multiple){
14189             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14190         }
14191         
14192         if(this.removable && !this.editable && !this.tickable){
14193             var close = this.closeTriggerEl();
14194             
14195             if(close){
14196                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14197                 close.on('click', this.removeBtnClick, this, close);
14198             }
14199         }
14200         
14201         //this.trigger.addClassOnOver('x-form-trigger-over');
14202         //this.trigger.addClassOnClick('x-form-trigger-click');
14203         
14204         //if(!this.width){
14205         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14206         //}
14207     },
14208     
14209     closeTriggerEl : function()
14210     {
14211         var close = this.el.select('.roo-combo-removable-btn', true).first();
14212         return close ? close : false;
14213     },
14214     
14215     removeBtnClick : function(e, h, el)
14216     {
14217         e.preventDefault();
14218         
14219         if(this.fireEvent("remove", this) !== false){
14220             this.reset();
14221             this.fireEvent("afterremove", this)
14222         }
14223     },
14224     
14225     createList : function()
14226     {
14227         this.list = Roo.get(document.body).createChild({
14228             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14229             cls: 'typeahead typeahead-long dropdown-menu shadow',
14230             style: 'display:none'
14231         });
14232         
14233         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14234         
14235     },
14236
14237     // private
14238     initTrigger : function(){
14239        
14240     },
14241
14242     // private
14243     onDestroy : function(){
14244         if(this.trigger){
14245             this.trigger.removeAllListeners();
14246           //  this.trigger.remove();
14247         }
14248         //if(this.wrap){
14249         //    this.wrap.remove();
14250         //}
14251         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14252     },
14253
14254     // private
14255     onFocus : function(){
14256         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14257         /*
14258         if(!this.mimicing){
14259             this.wrap.addClass('x-trigger-wrap-focus');
14260             this.mimicing = true;
14261             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14262             if(this.monitorTab){
14263                 this.el.on("keydown", this.checkTab, this);
14264             }
14265         }
14266         */
14267     },
14268
14269     // private
14270     checkTab : function(e){
14271         if(e.getKey() == e.TAB){
14272             this.triggerBlur();
14273         }
14274     },
14275
14276     // private
14277     onBlur : function(){
14278         // do nothing
14279     },
14280
14281     // private
14282     mimicBlur : function(e, t){
14283         /*
14284         if(!this.wrap.contains(t) && this.validateBlur()){
14285             this.triggerBlur();
14286         }
14287         */
14288     },
14289
14290     // private
14291     triggerBlur : function(){
14292         this.mimicing = false;
14293         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14294         if(this.monitorTab){
14295             this.el.un("keydown", this.checkTab, this);
14296         }
14297         //this.wrap.removeClass('x-trigger-wrap-focus');
14298         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14299     },
14300
14301     // private
14302     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14303     validateBlur : function(e, t){
14304         return true;
14305     },
14306
14307     // private
14308     onDisable : function(){
14309         this.inputEl().dom.disabled = true;
14310         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14311         //if(this.wrap){
14312         //    this.wrap.addClass('x-item-disabled');
14313         //}
14314     },
14315
14316     // private
14317     onEnable : function(){
14318         this.inputEl().dom.disabled = false;
14319         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14320         //if(this.wrap){
14321         //    this.el.removeClass('x-item-disabled');
14322         //}
14323     },
14324
14325     // private
14326     onShow : function(){
14327         var ae = this.getActionEl();
14328         
14329         if(ae){
14330             ae.dom.style.display = '';
14331             ae.dom.style.visibility = 'visible';
14332         }
14333     },
14334
14335     // private
14336     
14337     onHide : function(){
14338         var ae = this.getActionEl();
14339         ae.dom.style.display = 'none';
14340     },
14341
14342     /**
14343      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14344      * by an implementing function.
14345      * @method
14346      * @param {EventObject} e
14347      */
14348     onTriggerClick : Roo.emptyFn
14349 });
14350  
14351 /*
14352 * Licence: LGPL
14353 */
14354
14355 /**
14356  * @class Roo.bootstrap.form.CardUploader
14357  * @extends Roo.bootstrap.Button
14358  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14359  * @cfg {Number} errorTimeout default 3000
14360  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14361  * @cfg {Array}  html The button text.
14362
14363  *
14364  * @constructor
14365  * Create a new CardUploader
14366  * @param {Object} config The config object
14367  */
14368
14369 Roo.bootstrap.form.CardUploader = function(config){
14370     
14371  
14372     
14373     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14374     
14375     
14376     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14377         return r.data.id
14378      });
14379     
14380      this.addEvents({
14381          // raw events
14382         /**
14383          * @event preview
14384          * When a image is clicked on - and needs to display a slideshow or similar..
14385          * @param {Roo.bootstrap.Card} this
14386          * @param {Object} The image information data 
14387          *
14388          */
14389         'preview' : true,
14390          /**
14391          * @event download
14392          * When a the download link is clicked
14393          * @param {Roo.bootstrap.Card} this
14394          * @param {Object} The image information data  contains 
14395          */
14396         'download' : true
14397         
14398     });
14399 };
14400  
14401 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14402     
14403      
14404     errorTimeout : 3000,
14405      
14406     images : false,
14407    
14408     fileCollection : false,
14409     allowBlank : true,
14410     
14411     getAutoCreate : function()
14412     {
14413         
14414         var cfg =  {
14415             cls :'form-group' ,
14416             cn : [
14417                
14418                 {
14419                     tag: 'label',
14420                    //cls : 'input-group-addon',
14421                     html : this.fieldLabel
14422
14423                 },
14424
14425                 {
14426                     tag: 'input',
14427                     type : 'hidden',
14428                     name : this.name,
14429                     value : this.value,
14430                     cls : 'd-none  form-control'
14431                 },
14432                 
14433                 {
14434                     tag: 'input',
14435                     multiple : 'multiple',
14436                     type : 'file',
14437                     cls : 'd-none  roo-card-upload-selector'
14438                 },
14439                 
14440                 {
14441                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14442                 },
14443                 {
14444                     cls : 'card-columns roo-card-uploader-container'
14445                 }
14446
14447             ]
14448         };
14449            
14450          
14451         return cfg;
14452     },
14453     
14454     getChildContainer : function() /// what children are added to.
14455     {
14456         return this.containerEl;
14457     },
14458    
14459     getButtonContainer : function() /// what children are added to.
14460     {
14461         return this.el.select(".roo-card-uploader-button-container").first();
14462     },
14463    
14464     initEvents : function()
14465     {
14466         
14467         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14468         
14469         var t = this;
14470         this.addxtype({
14471             xns: Roo.bootstrap,
14472
14473             xtype : 'Button',
14474             container_method : 'getButtonContainer' ,            
14475             html :  this.html, // fix changable?
14476             cls : 'w-100 ',
14477             listeners : {
14478                 'click' : function(btn, e) {
14479                     t.onClick(e);
14480                 }
14481             }
14482         });
14483         
14484         
14485         
14486         
14487         this.urlAPI = (window.createObjectURL && window) || 
14488                                 (window.URL && URL.revokeObjectURL && URL) || 
14489                                 (window.webkitURL && webkitURL);
14490                         
14491          
14492          
14493          
14494         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14495         
14496         this.selectorEl.on('change', this.onFileSelected, this);
14497         if (this.images) {
14498             var t = this;
14499             this.images.forEach(function(img) {
14500                 t.addCard(img)
14501             });
14502             this.images = false;
14503         }
14504         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14505          
14506        
14507     },
14508     
14509    
14510     onClick : function(e)
14511     {
14512         e.preventDefault();
14513          
14514         this.selectorEl.dom.click();
14515          
14516     },
14517     
14518     onFileSelected : function(e)
14519     {
14520         e.preventDefault();
14521         
14522         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14523             return;
14524         }
14525         
14526         Roo.each(this.selectorEl.dom.files, function(file){    
14527             this.addFile(file);
14528         }, this);
14529          
14530     },
14531     
14532       
14533     
14534       
14535     
14536     addFile : function(file)
14537     {
14538            
14539         if(typeof(file) === 'string'){
14540             throw "Add file by name?"; // should not happen
14541             return;
14542         }
14543         
14544         if(!file || !this.urlAPI){
14545             return;
14546         }
14547         
14548         // file;
14549         // file.type;
14550         
14551         var _this = this;
14552         
14553         
14554         var url = _this.urlAPI.createObjectURL( file);
14555            
14556         this.addCard({
14557             id : Roo.bootstrap.form.CardUploader.ID--,
14558             is_uploaded : false,
14559             src : url,
14560             srcfile : file,
14561             title : file.name,
14562             mimetype : file.type,
14563             preview : false,
14564             is_deleted : 0
14565         });
14566         
14567     },
14568     
14569     /**
14570      * addCard - add an Attachment to the uploader
14571      * @param data - the data about the image to upload
14572      *
14573      * {
14574           id : 123
14575           title : "Title of file",
14576           is_uploaded : false,
14577           src : "http://.....",
14578           srcfile : { the File upload object },
14579           mimetype : file.type,
14580           preview : false,
14581           is_deleted : 0
14582           .. any other data...
14583         }
14584      *
14585      * 
14586     */
14587     
14588     addCard : function (data)
14589     {
14590         // hidden input element?
14591         // if the file is not an image...
14592         //then we need to use something other that and header_image
14593         var t = this;
14594         //   remove.....
14595         var footer = [
14596             {
14597                 xns : Roo.bootstrap,
14598                 xtype : 'CardFooter',
14599                  items: [
14600                     {
14601                         xns : Roo.bootstrap,
14602                         xtype : 'Element',
14603                         cls : 'd-flex',
14604                         items : [
14605                             
14606                             {
14607                                 xns : Roo.bootstrap,
14608                                 xtype : 'Button',
14609                                 html : String.format("<small>{0}</small>", data.title),
14610                                 cls : 'col-10 text-left',
14611                                 size: 'sm',
14612                                 weight: 'link',
14613                                 fa : 'download',
14614                                 listeners : {
14615                                     click : function() {
14616                                      
14617                                         t.fireEvent( "download", t, data );
14618                                     }
14619                                 }
14620                             },
14621                           
14622                             {
14623                                 xns : Roo.bootstrap,
14624                                 xtype : 'Button',
14625                                 style: 'max-height: 28px; ',
14626                                 size : 'sm',
14627                                 weight: 'danger',
14628                                 cls : 'col-2',
14629                                 fa : 'times',
14630                                 listeners : {
14631                                     click : function() {
14632                                         t.removeCard(data.id)
14633                                     }
14634                                 }
14635                             }
14636                         ]
14637                     }
14638                     
14639                 ] 
14640             }
14641             
14642         ];
14643         
14644         var cn = this.addxtype(
14645             {
14646                  
14647                 xns : Roo.bootstrap,
14648                 xtype : 'Card',
14649                 closeable : true,
14650                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14651                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14652                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14653                 data : data,
14654                 html : false,
14655                  
14656                 items : footer,
14657                 initEvents : function() {
14658                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14659                     var card = this;
14660                     this.imgEl = this.el.select('.card-img-top').first();
14661                     if (this.imgEl) {
14662                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14663                         this.imgEl.set({ 'pointer' : 'cursor' });
14664                                   
14665                     }
14666                     this.getCardFooter().addClass('p-1');
14667                     
14668                   
14669                 }
14670                 
14671             }
14672         );
14673         // dont' really need ot update items.
14674         // this.items.push(cn);
14675         this.fileCollection.add(cn);
14676         
14677         if (!data.srcfile) {
14678             this.updateInput();
14679             return;
14680         }
14681             
14682         var _t = this;
14683         var reader = new FileReader();
14684         reader.addEventListener("load", function() {  
14685             data.srcdata =  reader.result;
14686             _t.updateInput();
14687         });
14688         reader.readAsDataURL(data.srcfile);
14689         
14690         
14691         
14692     },
14693     removeCard : function(id)
14694     {
14695         
14696         var card  = this.fileCollection.get(id);
14697         card.data.is_deleted = 1;
14698         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14699         //this.fileCollection.remove(card);
14700         //this.items = this.items.filter(function(e) { return e != card });
14701         // dont' really need ot update items.
14702         card.el.dom.parentNode.removeChild(card.el.dom);
14703         this.updateInput();
14704
14705         
14706     },
14707     reset: function()
14708     {
14709         this.fileCollection.each(function(card) {
14710             if (card.el.dom && card.el.dom.parentNode) {
14711                 card.el.dom.parentNode.removeChild(card.el.dom);
14712             }
14713         });
14714         this.fileCollection.clear();
14715         this.updateInput();
14716     },
14717     
14718     updateInput : function()
14719     {
14720          var data = [];
14721         this.fileCollection.each(function(e) {
14722             data.push(e.data);
14723             
14724         });
14725         this.inputEl().dom.value = JSON.stringify(data);
14726         
14727         
14728         
14729     }
14730     
14731     
14732 });
14733
14734
14735 Roo.bootstrap.form.CardUploader.ID = -1;/*
14736  * Based on:
14737  * Ext JS Library 1.1.1
14738  * Copyright(c) 2006-2007, Ext JS, LLC.
14739  *
14740  * Originally Released Under LGPL - original licence link has changed is not relivant.
14741  *
14742  * Fork - LGPL
14743  * <script type="text/javascript">
14744  */
14745
14746
14747 /**
14748  * @class Roo.data.SortTypes
14749  * @static
14750  * Defines the default sorting (casting?) comparison functions used when sorting data.
14751  */
14752 Roo.data.SortTypes = {
14753     /**
14754      * Default sort that does nothing
14755      * @param {Mixed} s The value being converted
14756      * @return {Mixed} The comparison value
14757      */
14758     none : function(s){
14759         return s;
14760     },
14761     
14762     /**
14763      * The regular expression used to strip tags
14764      * @type {RegExp}
14765      * @property
14766      */
14767     stripTagsRE : /<\/?[^>]+>/gi,
14768     
14769     /**
14770      * Strips all HTML tags to sort on text only
14771      * @param {Mixed} s The value being converted
14772      * @return {String} The comparison value
14773      */
14774     asText : function(s){
14775         return String(s).replace(this.stripTagsRE, "");
14776     },
14777     
14778     /**
14779      * Strips all HTML tags to sort on text only - Case insensitive
14780      * @param {Mixed} s The value being converted
14781      * @return {String} The comparison value
14782      */
14783     asUCText : function(s){
14784         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14785     },
14786     
14787     /**
14788      * Case insensitive string
14789      * @param {Mixed} s The value being converted
14790      * @return {String} The comparison value
14791      */
14792     asUCString : function(s) {
14793         return String(s).toUpperCase();
14794     },
14795     
14796     /**
14797      * Date sorting
14798      * @param {Mixed} s The value being converted
14799      * @return {Number} The comparison value
14800      */
14801     asDate : function(s) {
14802         if(!s){
14803             return 0;
14804         }
14805         if(s instanceof Date){
14806             return s.getTime();
14807         }
14808         return Date.parse(String(s));
14809     },
14810     
14811     /**
14812      * Float sorting
14813      * @param {Mixed} s The value being converted
14814      * @return {Float} The comparison value
14815      */
14816     asFloat : function(s) {
14817         var val = parseFloat(String(s).replace(/,/g, ""));
14818         if(isNaN(val)) {
14819             val = 0;
14820         }
14821         return val;
14822     },
14823     
14824     /**
14825      * Integer sorting
14826      * @param {Mixed} s The value being converted
14827      * @return {Number} The comparison value
14828      */
14829     asInt : function(s) {
14830         var val = parseInt(String(s).replace(/,/g, ""));
14831         if(isNaN(val)) {
14832             val = 0;
14833         }
14834         return val;
14835     }
14836 };/*
14837  * Based on:
14838  * Ext JS Library 1.1.1
14839  * Copyright(c) 2006-2007, Ext JS, LLC.
14840  *
14841  * Originally Released Under LGPL - original licence link has changed is not relivant.
14842  *
14843  * Fork - LGPL
14844  * <script type="text/javascript">
14845  */
14846
14847 /**
14848 * @class Roo.data.Record
14849  * Instances of this class encapsulate both record <em>definition</em> information, and record
14850  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14851  * to access Records cached in an {@link Roo.data.Store} object.<br>
14852  * <p>
14853  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14854  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14855  * objects.<br>
14856  * <p>
14857  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14858  * @constructor
14859  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14860  * {@link #create}. The parameters are the same.
14861  * @param {Array} data An associative Array of data values keyed by the field name.
14862  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14863  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14864  * not specified an integer id is generated.
14865  */
14866 Roo.data.Record = function(data, id){
14867     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14868     this.data = data;
14869 };
14870
14871 /**
14872  * Generate a constructor for a specific record layout.
14873  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14874  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14875  * Each field definition object may contain the following properties: <ul>
14876  * <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,
14877  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14878  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14879  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14880  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14881  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14882  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14883  * this may be omitted.</p></li>
14884  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14885  * <ul><li>auto (Default, implies no conversion)</li>
14886  * <li>string</li>
14887  * <li>int</li>
14888  * <li>float</li>
14889  * <li>boolean</li>
14890  * <li>date</li></ul></p></li>
14891  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14892  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14893  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14894  * by the Reader into an object that will be stored in the Record. It is passed the
14895  * following parameters:<ul>
14896  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14897  * </ul></p></li>
14898  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14899  * </ul>
14900  * <br>usage:<br><pre><code>
14901 var TopicRecord = Roo.data.Record.create(
14902     {name: 'title', mapping: 'topic_title'},
14903     {name: 'author', mapping: 'username'},
14904     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14905     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14906     {name: 'lastPoster', mapping: 'user2'},
14907     {name: 'excerpt', mapping: 'post_text'}
14908 );
14909
14910 var myNewRecord = new TopicRecord({
14911     title: 'Do my job please',
14912     author: 'noobie',
14913     totalPosts: 1,
14914     lastPost: new Date(),
14915     lastPoster: 'Animal',
14916     excerpt: 'No way dude!'
14917 });
14918 myStore.add(myNewRecord);
14919 </code></pre>
14920  * @method create
14921  * @static
14922  */
14923 Roo.data.Record.create = function(o){
14924     var f = function(){
14925         f.superclass.constructor.apply(this, arguments);
14926     };
14927     Roo.extend(f, Roo.data.Record);
14928     var p = f.prototype;
14929     p.fields = new Roo.util.MixedCollection(false, function(field){
14930         return field.name;
14931     });
14932     for(var i = 0, len = o.length; i < len; i++){
14933         p.fields.add(new Roo.data.Field(o[i]));
14934     }
14935     f.getField = function(name){
14936         return p.fields.get(name);  
14937     };
14938     return f;
14939 };
14940
14941 Roo.data.Record.AUTO_ID = 1000;
14942 Roo.data.Record.EDIT = 'edit';
14943 Roo.data.Record.REJECT = 'reject';
14944 Roo.data.Record.COMMIT = 'commit';
14945
14946 Roo.data.Record.prototype = {
14947     /**
14948      * Readonly flag - true if this record has been modified.
14949      * @type Boolean
14950      */
14951     dirty : false,
14952     editing : false,
14953     error: null,
14954     modified: null,
14955
14956     // private
14957     join : function(store){
14958         this.store = store;
14959     },
14960
14961     /**
14962      * Set the named field to the specified value.
14963      * @param {String} name The name of the field to set.
14964      * @param {Object} value The value to set the field to.
14965      */
14966     set : function(name, value){
14967         if(this.data[name] == value){
14968             return;
14969         }
14970         this.dirty = true;
14971         if(!this.modified){
14972             this.modified = {};
14973         }
14974         if(typeof this.modified[name] == 'undefined'){
14975             this.modified[name] = this.data[name];
14976         }
14977         this.data[name] = value;
14978         if(!this.editing && this.store){
14979             this.store.afterEdit(this);
14980         }       
14981     },
14982
14983     /**
14984      * Get the value of the named field.
14985      * @param {String} name The name of the field to get the value of.
14986      * @return {Object} The value of the field.
14987      */
14988     get : function(name){
14989         return this.data[name]; 
14990     },
14991
14992     // private
14993     beginEdit : function(){
14994         this.editing = true;
14995         this.modified = {}; 
14996     },
14997
14998     // private
14999     cancelEdit : function(){
15000         this.editing = false;
15001         delete this.modified;
15002     },
15003
15004     // private
15005     endEdit : function(){
15006         this.editing = false;
15007         if(this.dirty && this.store){
15008             this.store.afterEdit(this);
15009         }
15010     },
15011
15012     /**
15013      * Usually called by the {@link Roo.data.Store} which owns the Record.
15014      * Rejects all changes made to the Record since either creation, or the last commit operation.
15015      * Modified fields are reverted to their original values.
15016      * <p>
15017      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15018      * of reject operations.
15019      */
15020     reject : function(){
15021         var m = this.modified;
15022         for(var n in m){
15023             if(typeof m[n] != "function"){
15024                 this.data[n] = m[n];
15025             }
15026         }
15027         this.dirty = false;
15028         delete this.modified;
15029         this.editing = false;
15030         if(this.store){
15031             this.store.afterReject(this);
15032         }
15033     },
15034
15035     /**
15036      * Usually called by the {@link Roo.data.Store} which owns the Record.
15037      * Commits all changes made to the Record since either creation, or the last commit operation.
15038      * <p>
15039      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15040      * of commit operations.
15041      */
15042     commit : function(){
15043         this.dirty = false;
15044         delete this.modified;
15045         this.editing = false;
15046         if(this.store){
15047             this.store.afterCommit(this);
15048         }
15049     },
15050
15051     // private
15052     hasError : function(){
15053         return this.error != null;
15054     },
15055
15056     // private
15057     clearError : function(){
15058         this.error = null;
15059     },
15060
15061     /**
15062      * Creates a copy of this record.
15063      * @param {String} id (optional) A new record id if you don't want to use this record's id
15064      * @return {Record}
15065      */
15066     copy : function(newId) {
15067         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15068     }
15069 };/*
15070  * Based on:
15071  * Ext JS Library 1.1.1
15072  * Copyright(c) 2006-2007, Ext JS, LLC.
15073  *
15074  * Originally Released Under LGPL - original licence link has changed is not relivant.
15075  *
15076  * Fork - LGPL
15077  * <script type="text/javascript">
15078  */
15079
15080
15081
15082 /**
15083  * @class Roo.data.Store
15084  * @extends Roo.util.Observable
15085  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15086  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15087  * <p>
15088  * 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
15089  * has no knowledge of the format of the data returned by the Proxy.<br>
15090  * <p>
15091  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15092  * instances from the data object. These records are cached and made available through accessor functions.
15093  * @constructor
15094  * Creates a new Store.
15095  * @param {Object} config A config object containing the objects needed for the Store to access data,
15096  * and read the data into Records.
15097  */
15098 Roo.data.Store = function(config){
15099     this.data = new Roo.util.MixedCollection(false);
15100     this.data.getKey = function(o){
15101         return o.id;
15102     };
15103     this.baseParams = {};
15104     // private
15105     this.paramNames = {
15106         "start" : "start",
15107         "limit" : "limit",
15108         "sort" : "sort",
15109         "dir" : "dir",
15110         "multisort" : "_multisort"
15111     };
15112
15113     if(config && config.data){
15114         this.inlineData = config.data;
15115         delete config.data;
15116     }
15117
15118     Roo.apply(this, config);
15119     
15120     if(this.reader){ // reader passed
15121         this.reader = Roo.factory(this.reader, Roo.data);
15122         this.reader.xmodule = this.xmodule || false;
15123         if(!this.recordType){
15124             this.recordType = this.reader.recordType;
15125         }
15126         if(this.reader.onMetaChange){
15127             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15128         }
15129     }
15130
15131     if(this.recordType){
15132         this.fields = this.recordType.prototype.fields;
15133     }
15134     this.modified = [];
15135
15136     this.addEvents({
15137         /**
15138          * @event datachanged
15139          * Fires when the data cache has changed, and a widget which is using this Store
15140          * as a Record cache should refresh its view.
15141          * @param {Store} this
15142          */
15143         datachanged : true,
15144         /**
15145          * @event metachange
15146          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15147          * @param {Store} this
15148          * @param {Object} meta The JSON metadata
15149          */
15150         metachange : true,
15151         /**
15152          * @event add
15153          * Fires when Records have been added to the Store
15154          * @param {Store} this
15155          * @param {Roo.data.Record[]} records The array of Records added
15156          * @param {Number} index The index at which the record(s) were added
15157          */
15158         add : true,
15159         /**
15160          * @event remove
15161          * Fires when a Record has been removed from the Store
15162          * @param {Store} this
15163          * @param {Roo.data.Record} record The Record that was removed
15164          * @param {Number} index The index at which the record was removed
15165          */
15166         remove : true,
15167         /**
15168          * @event update
15169          * Fires when a Record has been updated
15170          * @param {Store} this
15171          * @param {Roo.data.Record} record The Record that was updated
15172          * @param {String} operation The update operation being performed.  Value may be one of:
15173          * <pre><code>
15174  Roo.data.Record.EDIT
15175  Roo.data.Record.REJECT
15176  Roo.data.Record.COMMIT
15177          * </code></pre>
15178          */
15179         update : true,
15180         /**
15181          * @event clear
15182          * Fires when the data cache has been cleared.
15183          * @param {Store} this
15184          */
15185         clear : true,
15186         /**
15187          * @event beforeload
15188          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15189          * the load action will be canceled.
15190          * @param {Store} this
15191          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15192          */
15193         beforeload : true,
15194         /**
15195          * @event beforeloadadd
15196          * Fires after a new set of Records has been loaded.
15197          * @param {Store} this
15198          * @param {Roo.data.Record[]} records The Records that were loaded
15199          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15200          */
15201         beforeloadadd : true,
15202         /**
15203          * @event load
15204          * Fires after a new set of Records has been loaded, before they are added to the store.
15205          * @param {Store} this
15206          * @param {Roo.data.Record[]} records The Records that were loaded
15207          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15208          * @params {Object} return from reader
15209          */
15210         load : true,
15211         /**
15212          * @event loadexception
15213          * Fires if an exception occurs in the Proxy during loading.
15214          * Called with the signature of the Proxy's "loadexception" event.
15215          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15216          * 
15217          * @param {Proxy} 
15218          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15219          * @param {Object} load options 
15220          * @param {Object} jsonData from your request (normally this contains the Exception)
15221          */
15222         loadexception : true
15223     });
15224     
15225     if(this.proxy){
15226         this.proxy = Roo.factory(this.proxy, Roo.data);
15227         this.proxy.xmodule = this.xmodule || false;
15228         this.relayEvents(this.proxy,  ["loadexception"]);
15229     }
15230     this.sortToggle = {};
15231     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15232
15233     Roo.data.Store.superclass.constructor.call(this);
15234
15235     if(this.inlineData){
15236         this.loadData(this.inlineData);
15237         delete this.inlineData;
15238     }
15239 };
15240
15241 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15242      /**
15243     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15244     * without a remote query - used by combo/forms at present.
15245     */
15246     
15247     /**
15248     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15249     */
15250     /**
15251     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15252     */
15253     /**
15254     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15255     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15256     */
15257     /**
15258     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15259     * on any HTTP request
15260     */
15261     /**
15262     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15263     */
15264     /**
15265     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15266     */
15267     multiSort: false,
15268     /**
15269     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15270     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15271     */
15272     remoteSort : false,
15273
15274     /**
15275     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15276      * loaded or when a record is removed. (defaults to false).
15277     */
15278     pruneModifiedRecords : false,
15279
15280     // private
15281     lastOptions : null,
15282
15283     /**
15284      * Add Records to the Store and fires the add event.
15285      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15286      */
15287     add : function(records){
15288         records = [].concat(records);
15289         for(var i = 0, len = records.length; i < len; i++){
15290             records[i].join(this);
15291         }
15292         var index = this.data.length;
15293         this.data.addAll(records);
15294         this.fireEvent("add", this, records, index);
15295     },
15296
15297     /**
15298      * Remove a Record from the Store and fires the remove event.
15299      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15300      */
15301     remove : function(record){
15302         var index = this.data.indexOf(record);
15303         this.data.removeAt(index);
15304  
15305         if(this.pruneModifiedRecords){
15306             this.modified.remove(record);
15307         }
15308         this.fireEvent("remove", this, record, index);
15309     },
15310
15311     /**
15312      * Remove all Records from the Store and fires the clear event.
15313      */
15314     removeAll : function(){
15315         this.data.clear();
15316         if(this.pruneModifiedRecords){
15317             this.modified = [];
15318         }
15319         this.fireEvent("clear", this);
15320     },
15321
15322     /**
15323      * Inserts Records to the Store at the given index and fires the add event.
15324      * @param {Number} index The start index at which to insert the passed Records.
15325      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15326      */
15327     insert : function(index, records){
15328         records = [].concat(records);
15329         for(var i = 0, len = records.length; i < len; i++){
15330             this.data.insert(index, records[i]);
15331             records[i].join(this);
15332         }
15333         this.fireEvent("add", this, records, index);
15334     },
15335
15336     /**
15337      * Get the index within the cache of the passed Record.
15338      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15339      * @return {Number} The index of the passed Record. Returns -1 if not found.
15340      */
15341     indexOf : function(record){
15342         return this.data.indexOf(record);
15343     },
15344
15345     /**
15346      * Get the index within the cache of the Record with the passed id.
15347      * @param {String} id The id of the Record to find.
15348      * @return {Number} The index of the Record. Returns -1 if not found.
15349      */
15350     indexOfId : function(id){
15351         return this.data.indexOfKey(id);
15352     },
15353
15354     /**
15355      * Get the Record with the specified id.
15356      * @param {String} id The id of the Record to find.
15357      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15358      */
15359     getById : function(id){
15360         return this.data.key(id);
15361     },
15362
15363     /**
15364      * Get the Record at the specified index.
15365      * @param {Number} index The index of the Record to find.
15366      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15367      */
15368     getAt : function(index){
15369         return this.data.itemAt(index);
15370     },
15371
15372     /**
15373      * Returns a range of Records between specified indices.
15374      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15375      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15376      * @return {Roo.data.Record[]} An array of Records
15377      */
15378     getRange : function(start, end){
15379         return this.data.getRange(start, end);
15380     },
15381
15382     // private
15383     storeOptions : function(o){
15384         o = Roo.apply({}, o);
15385         delete o.callback;
15386         delete o.scope;
15387         this.lastOptions = o;
15388     },
15389
15390     /**
15391      * Loads the Record cache from the configured Proxy using the configured Reader.
15392      * <p>
15393      * If using remote paging, then the first load call must specify the <em>start</em>
15394      * and <em>limit</em> properties in the options.params property to establish the initial
15395      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15396      * <p>
15397      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15398      * and this call will return before the new data has been loaded. Perform any post-processing
15399      * in a callback function, or in a "load" event handler.</strong>
15400      * <p>
15401      * @param {Object} options An object containing properties which control loading options:<ul>
15402      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15403      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15404      * <pre>
15405                 {
15406                     data : data,  // array of key=>value data like JsonReader
15407                     total : data.length,
15408                     success : true
15409                     
15410                 }
15411         </pre>
15412             }.</li>
15413      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15414      * passed the following arguments:<ul>
15415      * <li>r : Roo.data.Record[]</li>
15416      * <li>options: Options object from the load call</li>
15417      * <li>success: Boolean success indicator</li></ul></li>
15418      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15419      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15420      * </ul>
15421      */
15422     load : function(options){
15423         options = options || {};
15424         if(this.fireEvent("beforeload", this, options) !== false){
15425             this.storeOptions(options);
15426             var p = Roo.apply(options.params || {}, this.baseParams);
15427             // if meta was not loaded from remote source.. try requesting it.
15428             if (!this.reader.metaFromRemote) {
15429                 p._requestMeta = 1;
15430             }
15431             if(this.sortInfo && this.remoteSort){
15432                 var pn = this.paramNames;
15433                 p[pn["sort"]] = this.sortInfo.field;
15434                 p[pn["dir"]] = this.sortInfo.direction;
15435             }
15436             if (this.multiSort) {
15437                 var pn = this.paramNames;
15438                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15439             }
15440             
15441             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15442         }
15443     },
15444
15445     /**
15446      * Reloads the Record cache from the configured Proxy using the configured Reader and
15447      * the options from the last load operation performed.
15448      * @param {Object} options (optional) An object containing properties which may override the options
15449      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15450      * the most recently used options are reused).
15451      */
15452     reload : function(options){
15453         this.load(Roo.applyIf(options||{}, this.lastOptions));
15454     },
15455
15456     // private
15457     // Called as a callback by the Reader during a load operation.
15458     loadRecords : function(o, options, success){
15459          
15460         if(!o){
15461             if(success !== false){
15462                 this.fireEvent("load", this, [], options, o);
15463             }
15464             if(options.callback){
15465                 options.callback.call(options.scope || this, [], options, false);
15466             }
15467             return;
15468         }
15469         // if data returned failure - throw an exception.
15470         if (o.success === false) {
15471             // show a message if no listener is registered.
15472             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15473                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15474             }
15475             // loadmask wil be hooked into this..
15476             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15477             return;
15478         }
15479         var r = o.records, t = o.totalRecords || r.length;
15480         
15481         this.fireEvent("beforeloadadd", this, r, options, o);
15482         
15483         if(!options || options.add !== true){
15484             if(this.pruneModifiedRecords){
15485                 this.modified = [];
15486             }
15487             for(var i = 0, len = r.length; i < len; i++){
15488                 r[i].join(this);
15489             }
15490             if(this.snapshot){
15491                 this.data = this.snapshot;
15492                 delete this.snapshot;
15493             }
15494             this.data.clear();
15495             this.data.addAll(r);
15496             this.totalLength = t;
15497             this.applySort();
15498             this.fireEvent("datachanged", this);
15499         }else{
15500             this.totalLength = Math.max(t, this.data.length+r.length);
15501             this.add(r);
15502         }
15503         
15504         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15505                 
15506             var e = new Roo.data.Record({});
15507
15508             e.set(this.parent.displayField, this.parent.emptyTitle);
15509             e.set(this.parent.valueField, '');
15510
15511             this.insert(0, e);
15512         }
15513             
15514         this.fireEvent("load", this, r, options, o);
15515         if(options.callback){
15516             options.callback.call(options.scope || this, r, options, true);
15517         }
15518     },
15519
15520
15521     /**
15522      * Loads data from a passed data block. A Reader which understands the format of the data
15523      * must have been configured in the constructor.
15524      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15525      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15526      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15527      */
15528     loadData : function(o, append){
15529         var r = this.reader.readRecords(o);
15530         this.loadRecords(r, {add: append}, true);
15531     },
15532     
15533      /**
15534      * using 'cn' the nested child reader read the child array into it's child stores.
15535      * @param {Object} rec The record with a 'children array
15536      */
15537     loadDataFromChildren : function(rec)
15538     {
15539         this.loadData(this.reader.toLoadData(rec));
15540     },
15541     
15542
15543     /**
15544      * Gets the number of cached records.
15545      * <p>
15546      * <em>If using paging, this may not be the total size of the dataset. If the data object
15547      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15548      * the data set size</em>
15549      */
15550     getCount : function(){
15551         return this.data.length || 0;
15552     },
15553
15554     /**
15555      * Gets the total number of records in the dataset as returned by the server.
15556      * <p>
15557      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15558      * the dataset size</em>
15559      */
15560     getTotalCount : function(){
15561         return this.totalLength || 0;
15562     },
15563
15564     /**
15565      * Returns the sort state of the Store as an object with two properties:
15566      * <pre><code>
15567  field {String} The name of the field by which the Records are sorted
15568  direction {String} The sort order, "ASC" or "DESC"
15569      * </code></pre>
15570      */
15571     getSortState : function(){
15572         return this.sortInfo;
15573     },
15574
15575     // private
15576     applySort : function(){
15577         if(this.sortInfo && !this.remoteSort){
15578             var s = this.sortInfo, f = s.field;
15579             var st = this.fields.get(f).sortType;
15580             var fn = function(r1, r2){
15581                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15582                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15583             };
15584             this.data.sort(s.direction, fn);
15585             if(this.snapshot && this.snapshot != this.data){
15586                 this.snapshot.sort(s.direction, fn);
15587             }
15588         }
15589     },
15590
15591     /**
15592      * Sets the default sort column and order to be used by the next load operation.
15593      * @param {String} fieldName The name of the field to sort by.
15594      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15595      */
15596     setDefaultSort : function(field, dir){
15597         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15598     },
15599
15600     /**
15601      * Sort the Records.
15602      * If remote sorting is used, the sort is performed on the server, and the cache is
15603      * reloaded. If local sorting is used, the cache is sorted internally.
15604      * @param {String} fieldName The name of the field to sort by.
15605      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15606      */
15607     sort : function(fieldName, dir){
15608         var f = this.fields.get(fieldName);
15609         if(!dir){
15610             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15611             
15612             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15613                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15614             }else{
15615                 dir = f.sortDir;
15616             }
15617         }
15618         this.sortToggle[f.name] = dir;
15619         this.sortInfo = {field: f.name, direction: dir};
15620         if(!this.remoteSort){
15621             this.applySort();
15622             this.fireEvent("datachanged", this);
15623         }else{
15624             this.load(this.lastOptions);
15625         }
15626     },
15627
15628     /**
15629      * Calls the specified function for each of the Records in the cache.
15630      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15631      * Returning <em>false</em> aborts and exits the iteration.
15632      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15633      */
15634     each : function(fn, scope){
15635         this.data.each(fn, scope);
15636     },
15637
15638     /**
15639      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15640      * (e.g., during paging).
15641      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15642      */
15643     getModifiedRecords : function(){
15644         return this.modified;
15645     },
15646
15647     // private
15648     createFilterFn : function(property, value, anyMatch){
15649         if(!value.exec){ // not a regex
15650             value = String(value);
15651             if(value.length == 0){
15652                 return false;
15653             }
15654             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15655         }
15656         return function(r){
15657             return value.test(r.data[property]);
15658         };
15659     },
15660
15661     /**
15662      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15663      * @param {String} property A field on your records
15664      * @param {Number} start The record index to start at (defaults to 0)
15665      * @param {Number} end The last record index to include (defaults to length - 1)
15666      * @return {Number} The sum
15667      */
15668     sum : function(property, start, end){
15669         var rs = this.data.items, v = 0;
15670         start = start || 0;
15671         end = (end || end === 0) ? end : rs.length-1;
15672
15673         for(var i = start; i <= end; i++){
15674             v += (rs[i].data[property] || 0);
15675         }
15676         return v;
15677     },
15678
15679     /**
15680      * Filter the records by a specified property.
15681      * @param {String} field A field on your records
15682      * @param {String/RegExp} value Either a string that the field
15683      * should start with or a RegExp to test against the field
15684      * @param {Boolean} anyMatch True to match any part not just the beginning
15685      */
15686     filter : function(property, value, anyMatch){
15687         var fn = this.createFilterFn(property, value, anyMatch);
15688         return fn ? this.filterBy(fn) : this.clearFilter();
15689     },
15690
15691     /**
15692      * Filter by a function. The specified function will be called with each
15693      * record in this data source. If the function returns true the record is included,
15694      * otherwise it is filtered.
15695      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15696      * @param {Object} scope (optional) The scope of the function (defaults to this)
15697      */
15698     filterBy : function(fn, scope){
15699         this.snapshot = this.snapshot || this.data;
15700         this.data = this.queryBy(fn, scope||this);
15701         this.fireEvent("datachanged", this);
15702     },
15703
15704     /**
15705      * Query the records by a specified property.
15706      * @param {String} field A field on your records
15707      * @param {String/RegExp} value Either a string that the field
15708      * should start with or a RegExp to test against the field
15709      * @param {Boolean} anyMatch True to match any part not just the beginning
15710      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15711      */
15712     query : function(property, value, anyMatch){
15713         var fn = this.createFilterFn(property, value, anyMatch);
15714         return fn ? this.queryBy(fn) : this.data.clone();
15715     },
15716
15717     /**
15718      * Query by a function. The specified function will be called with each
15719      * record in this data source. If the function returns true the record is included
15720      * in the results.
15721      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15722      * @param {Object} scope (optional) The scope of the function (defaults to this)
15723       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15724      **/
15725     queryBy : function(fn, scope){
15726         var data = this.snapshot || this.data;
15727         return data.filterBy(fn, scope||this);
15728     },
15729
15730     /**
15731      * Collects unique values for a particular dataIndex from this store.
15732      * @param {String} dataIndex The property to collect
15733      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15734      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15735      * @return {Array} An array of the unique values
15736      **/
15737     collect : function(dataIndex, allowNull, bypassFilter){
15738         var d = (bypassFilter === true && this.snapshot) ?
15739                 this.snapshot.items : this.data.items;
15740         var v, sv, r = [], l = {};
15741         for(var i = 0, len = d.length; i < len; i++){
15742             v = d[i].data[dataIndex];
15743             sv = String(v);
15744             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15745                 l[sv] = true;
15746                 r[r.length] = v;
15747             }
15748         }
15749         return r;
15750     },
15751
15752     /**
15753      * Revert to a view of the Record cache with no filtering applied.
15754      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15755      */
15756     clearFilter : function(suppressEvent){
15757         if(this.snapshot && this.snapshot != this.data){
15758             this.data = this.snapshot;
15759             delete this.snapshot;
15760             if(suppressEvent !== true){
15761                 this.fireEvent("datachanged", this);
15762             }
15763         }
15764     },
15765
15766     // private
15767     afterEdit : function(record){
15768         if(this.modified.indexOf(record) == -1){
15769             this.modified.push(record);
15770         }
15771         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15772     },
15773     
15774     // private
15775     afterReject : function(record){
15776         this.modified.remove(record);
15777         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15778     },
15779
15780     // private
15781     afterCommit : function(record){
15782         this.modified.remove(record);
15783         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15784     },
15785
15786     /**
15787      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15788      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15789      */
15790     commitChanges : function(){
15791         var m = this.modified.slice(0);
15792         this.modified = [];
15793         for(var i = 0, len = m.length; i < len; i++){
15794             m[i].commit();
15795         }
15796     },
15797
15798     /**
15799      * Cancel outstanding changes on all changed records.
15800      */
15801     rejectChanges : function(){
15802         var m = this.modified.slice(0);
15803         this.modified = [];
15804         for(var i = 0, len = m.length; i < len; i++){
15805             m[i].reject();
15806         }
15807     },
15808
15809     onMetaChange : function(meta, rtype, o){
15810         this.recordType = rtype;
15811         this.fields = rtype.prototype.fields;
15812         delete this.snapshot;
15813         this.sortInfo = meta.sortInfo || this.sortInfo;
15814         this.modified = [];
15815         this.fireEvent('metachange', this, this.reader.meta);
15816     },
15817     
15818     moveIndex : function(data, type)
15819     {
15820         var index = this.indexOf(data);
15821         
15822         var newIndex = index + type;
15823         
15824         this.remove(data);
15825         
15826         this.insert(newIndex, data);
15827         
15828     }
15829 });/*
15830  * Based on:
15831  * Ext JS Library 1.1.1
15832  * Copyright(c) 2006-2007, Ext JS, LLC.
15833  *
15834  * Originally Released Under LGPL - original licence link has changed is not relivant.
15835  *
15836  * Fork - LGPL
15837  * <script type="text/javascript">
15838  */
15839
15840 /**
15841  * @class Roo.data.SimpleStore
15842  * @extends Roo.data.Store
15843  * Small helper class to make creating Stores from Array data easier.
15844  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15845  * @cfg {Array} fields An array of field definition objects, or field name strings.
15846  * @cfg {Object} an existing reader (eg. copied from another store)
15847  * @cfg {Array} data The multi-dimensional array of data
15848  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15849  * @cfg {Roo.data.Reader} reader  [not-required] 
15850  * @constructor
15851  * @param {Object} config
15852  */
15853 Roo.data.SimpleStore = function(config)
15854 {
15855     Roo.data.SimpleStore.superclass.constructor.call(this, {
15856         isLocal : true,
15857         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15858                 id: config.id
15859             },
15860             Roo.data.Record.create(config.fields)
15861         ),
15862         proxy : new Roo.data.MemoryProxy(config.data)
15863     });
15864     this.load();
15865 };
15866 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15867  * Based on:
15868  * Ext JS Library 1.1.1
15869  * Copyright(c) 2006-2007, Ext JS, LLC.
15870  *
15871  * Originally Released Under LGPL - original licence link has changed is not relivant.
15872  *
15873  * Fork - LGPL
15874  * <script type="text/javascript">
15875  */
15876
15877 /**
15878 /**
15879  * @extends Roo.data.Store
15880  * @class Roo.data.JsonStore
15881  * Small helper class to make creating Stores for JSON data easier. <br/>
15882 <pre><code>
15883 var store = new Roo.data.JsonStore({
15884     url: 'get-images.php',
15885     root: 'images',
15886     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15887 });
15888 </code></pre>
15889  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15890  * JsonReader and HttpProxy (unless inline data is provided).</b>
15891  * @cfg {Array} fields An array of field definition objects, or field name strings.
15892  * @constructor
15893  * @param {Object} config
15894  */
15895 Roo.data.JsonStore = function(c){
15896     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15897         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15898         reader: new Roo.data.JsonReader(c, c.fields)
15899     }));
15900 };
15901 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15902  * Based on:
15903  * Ext JS Library 1.1.1
15904  * Copyright(c) 2006-2007, Ext JS, LLC.
15905  *
15906  * Originally Released Under LGPL - original licence link has changed is not relivant.
15907  *
15908  * Fork - LGPL
15909  * <script type="text/javascript">
15910  */
15911
15912  
15913 Roo.data.Field = function(config){
15914     if(typeof config == "string"){
15915         config = {name: config};
15916     }
15917     Roo.apply(this, config);
15918     
15919     if(!this.type){
15920         this.type = "auto";
15921     }
15922     
15923     var st = Roo.data.SortTypes;
15924     // named sortTypes are supported, here we look them up
15925     if(typeof this.sortType == "string"){
15926         this.sortType = st[this.sortType];
15927     }
15928     
15929     // set default sortType for strings and dates
15930     if(!this.sortType){
15931         switch(this.type){
15932             case "string":
15933                 this.sortType = st.asUCString;
15934                 break;
15935             case "date":
15936                 this.sortType = st.asDate;
15937                 break;
15938             default:
15939                 this.sortType = st.none;
15940         }
15941     }
15942
15943     // define once
15944     var stripRe = /[\$,%]/g;
15945
15946     // prebuilt conversion function for this field, instead of
15947     // switching every time we're reading a value
15948     if(!this.convert){
15949         var cv, dateFormat = this.dateFormat;
15950         switch(this.type){
15951             case "":
15952             case "auto":
15953             case undefined:
15954                 cv = function(v){ return v; };
15955                 break;
15956             case "string":
15957                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15958                 break;
15959             case "int":
15960                 cv = function(v){
15961                     return v !== undefined && v !== null && v !== '' ?
15962                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15963                     };
15964                 break;
15965             case "float":
15966                 cv = function(v){
15967                     return v !== undefined && v !== null && v !== '' ?
15968                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15969                     };
15970                 break;
15971             case "bool":
15972             case "boolean":
15973                 cv = function(v){ return v === true || v === "true" || v == 1; };
15974                 break;
15975             case "date":
15976                 cv = function(v){
15977                     if(!v){
15978                         return '';
15979                     }
15980                     if(v instanceof Date){
15981                         return v;
15982                     }
15983                     if(dateFormat){
15984                         if(dateFormat == "timestamp"){
15985                             return new Date(v*1000);
15986                         }
15987                         return Date.parseDate(v, dateFormat);
15988                     }
15989                     var parsed = Date.parse(v);
15990                     return parsed ? new Date(parsed) : null;
15991                 };
15992              break;
15993             
15994         }
15995         this.convert = cv;
15996     }
15997 };
15998
15999 Roo.data.Field.prototype = {
16000     dateFormat: null,
16001     defaultValue: "",
16002     mapping: null,
16003     sortType : null,
16004     sortDir : "ASC"
16005 };/*
16006  * Based on:
16007  * Ext JS Library 1.1.1
16008  * Copyright(c) 2006-2007, Ext JS, LLC.
16009  *
16010  * Originally Released Under LGPL - original licence link has changed is not relivant.
16011  *
16012  * Fork - LGPL
16013  * <script type="text/javascript">
16014  */
16015  
16016 // Base class for reading structured data from a data source.  This class is intended to be
16017 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
16018
16019 /**
16020  * @class Roo.data.DataReader
16021  * @abstract
16022  * Base class for reading structured data from a data source.  This class is intended to be
16023  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16024  */
16025
16026 Roo.data.DataReader = function(meta, recordType){
16027     
16028     this.meta = meta;
16029     
16030     this.recordType = recordType instanceof Array ? 
16031         Roo.data.Record.create(recordType) : recordType;
16032 };
16033
16034 Roo.data.DataReader.prototype = {
16035     
16036     
16037     readerType : 'Data',
16038      /**
16039      * Create an empty record
16040      * @param {Object} data (optional) - overlay some values
16041      * @return {Roo.data.Record} record created.
16042      */
16043     newRow :  function(d) {
16044         var da =  {};
16045         this.recordType.prototype.fields.each(function(c) {
16046             switch( c.type) {
16047                 case 'int' : da[c.name] = 0; break;
16048                 case 'date' : da[c.name] = new Date(); break;
16049                 case 'float' : da[c.name] = 0.0; break;
16050                 case 'boolean' : da[c.name] = false; break;
16051                 default : da[c.name] = ""; break;
16052             }
16053             
16054         });
16055         return new this.recordType(Roo.apply(da, d));
16056     }
16057     
16058     
16059 };/*
16060  * Based on:
16061  * Ext JS Library 1.1.1
16062  * Copyright(c) 2006-2007, Ext JS, LLC.
16063  *
16064  * Originally Released Under LGPL - original licence link has changed is not relivant.
16065  *
16066  * Fork - LGPL
16067  * <script type="text/javascript">
16068  */
16069
16070 /**
16071  * @class Roo.data.DataProxy
16072  * @extends Roo.util.Observable
16073  * @abstract
16074  * This class is an abstract base class for implementations which provide retrieval of
16075  * unformatted data objects.<br>
16076  * <p>
16077  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16078  * (of the appropriate type which knows how to parse the data object) to provide a block of
16079  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16080  * <p>
16081  * Custom implementations must implement the load method as described in
16082  * {@link Roo.data.HttpProxy#load}.
16083  */
16084 Roo.data.DataProxy = function(){
16085     this.addEvents({
16086         /**
16087          * @event beforeload
16088          * Fires before a network request is made to retrieve a data object.
16089          * @param {Object} This DataProxy object.
16090          * @param {Object} params The params parameter to the load function.
16091          */
16092         beforeload : true,
16093         /**
16094          * @event load
16095          * Fires before the load method's callback is called.
16096          * @param {Object} This DataProxy object.
16097          * @param {Object} o The data object.
16098          * @param {Object} arg The callback argument object passed to the load function.
16099          */
16100         load : true,
16101         /**
16102          * @event loadexception
16103          * Fires if an Exception occurs during data retrieval.
16104          * @param {Object} This DataProxy object.
16105          * @param {Object} o The data object.
16106          * @param {Object} arg The callback argument object passed to the load function.
16107          * @param {Object} e The Exception.
16108          */
16109         loadexception : true
16110     });
16111     Roo.data.DataProxy.superclass.constructor.call(this);
16112 };
16113
16114 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16115
16116     /**
16117      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16118      */
16119 /*
16120  * Based on:
16121  * Ext JS Library 1.1.1
16122  * Copyright(c) 2006-2007, Ext JS, LLC.
16123  *
16124  * Originally Released Under LGPL - original licence link has changed is not relivant.
16125  *
16126  * Fork - LGPL
16127  * <script type="text/javascript">
16128  */
16129 /**
16130  * @class Roo.data.MemoryProxy
16131  * @extends Roo.data.DataProxy
16132  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16133  * to the Reader when its load method is called.
16134  * @constructor
16135  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16136  */
16137 Roo.data.MemoryProxy = function(config){
16138     var data = config;
16139     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16140         data = config.data;
16141     }
16142     Roo.data.MemoryProxy.superclass.constructor.call(this);
16143     this.data = data;
16144 };
16145
16146 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16147     
16148     /**
16149      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16150      */
16151     /**
16152      * Load data from the requested source (in this case an in-memory
16153      * data object passed to the constructor), read the data object into
16154      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16155      * process that block using the passed callback.
16156      * @param {Object} params This parameter is not used by the MemoryProxy class.
16157      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16158      * object into a block of Roo.data.Records.
16159      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16160      * The function must be passed <ul>
16161      * <li>The Record block object</li>
16162      * <li>The "arg" argument from the load function</li>
16163      * <li>A boolean success indicator</li>
16164      * </ul>
16165      * @param {Object} scope The scope in which to call the callback
16166      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16167      */
16168     load : function(params, reader, callback, scope, arg){
16169         params = params || {};
16170         var result;
16171         try {
16172             result = reader.readRecords(params.data ? params.data :this.data);
16173         }catch(e){
16174             this.fireEvent("loadexception", this, arg, null, e);
16175             callback.call(scope, null, arg, false);
16176             return;
16177         }
16178         callback.call(scope, result, arg, true);
16179     },
16180     
16181     // private
16182     update : function(params, records){
16183         
16184     }
16185 });/*
16186  * Based on:
16187  * Ext JS Library 1.1.1
16188  * Copyright(c) 2006-2007, Ext JS, LLC.
16189  *
16190  * Originally Released Under LGPL - original licence link has changed is not relivant.
16191  *
16192  * Fork - LGPL
16193  * <script type="text/javascript">
16194  */
16195 /**
16196  * @class Roo.data.HttpProxy
16197  * @extends Roo.data.DataProxy
16198  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16199  * configured to reference a certain URL.<br><br>
16200  * <p>
16201  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16202  * from which the running page was served.<br><br>
16203  * <p>
16204  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16205  * <p>
16206  * Be aware that to enable the browser to parse an XML document, the server must set
16207  * the Content-Type header in the HTTP response to "text/xml".
16208  * @constructor
16209  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16210  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16211  * will be used to make the request.
16212  */
16213 Roo.data.HttpProxy = function(conn){
16214     Roo.data.HttpProxy.superclass.constructor.call(this);
16215     // is conn a conn config or a real conn?
16216     this.conn = conn;
16217     this.useAjax = !conn || !conn.events;
16218   
16219 };
16220
16221 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16222     // thse are take from connection...
16223     
16224     /**
16225      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16226      */
16227     /**
16228      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16229      * extra parameters to each request made by this object. (defaults to undefined)
16230      */
16231     /**
16232      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16233      *  to each request made by this object. (defaults to undefined)
16234      */
16235     /**
16236      * @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)
16237      */
16238     /**
16239      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16240      */
16241      /**
16242      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16243      * @type Boolean
16244      */
16245   
16246
16247     /**
16248      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16249      * @type Boolean
16250      */
16251     /**
16252      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16253      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16254      * a finer-grained basis than the DataProxy events.
16255      */
16256     getConnection : function(){
16257         return this.useAjax ? Roo.Ajax : this.conn;
16258     },
16259
16260     /**
16261      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16262      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16263      * process that block using the passed callback.
16264      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16265      * for the request to the remote server.
16266      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16267      * object into a block of Roo.data.Records.
16268      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16269      * The function must be passed <ul>
16270      * <li>The Record block object</li>
16271      * <li>The "arg" argument from the load function</li>
16272      * <li>A boolean success indicator</li>
16273      * </ul>
16274      * @param {Object} scope The scope in which to call the callback
16275      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16276      */
16277     load : function(params, reader, callback, scope, arg){
16278         if(this.fireEvent("beforeload", this, params) !== false){
16279             var  o = {
16280                 params : params || {},
16281                 request: {
16282                     callback : callback,
16283                     scope : scope,
16284                     arg : arg
16285                 },
16286                 reader: reader,
16287                 callback : this.loadResponse,
16288                 scope: this
16289             };
16290             if(this.useAjax){
16291                 Roo.applyIf(o, this.conn);
16292                 if(this.activeRequest){
16293                     Roo.Ajax.abort(this.activeRequest);
16294                 }
16295                 this.activeRequest = Roo.Ajax.request(o);
16296             }else{
16297                 this.conn.request(o);
16298             }
16299         }else{
16300             callback.call(scope||this, null, arg, false);
16301         }
16302     },
16303
16304     // private
16305     loadResponse : function(o, success, response){
16306         delete this.activeRequest;
16307         if(!success){
16308             this.fireEvent("loadexception", this, o, response);
16309             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16310             return;
16311         }
16312         var result;
16313         try {
16314             result = o.reader.read(response);
16315         }catch(e){
16316             o.success = false;
16317             o.raw = { errorMsg : response.responseText };
16318             this.fireEvent("loadexception", this, o, response, e);
16319             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16320             return;
16321         }
16322         
16323         this.fireEvent("load", this, o, o.request.arg);
16324         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16325     },
16326
16327     // private
16328     update : function(dataSet){
16329
16330     },
16331
16332     // private
16333     updateResponse : function(dataSet){
16334
16335     }
16336 });/*
16337  * Based on:
16338  * Ext JS Library 1.1.1
16339  * Copyright(c) 2006-2007, Ext JS, LLC.
16340  *
16341  * Originally Released Under LGPL - original licence link has changed is not relivant.
16342  *
16343  * Fork - LGPL
16344  * <script type="text/javascript">
16345  */
16346
16347 /**
16348  * @class Roo.data.ScriptTagProxy
16349  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16350  * other than the originating domain of the running page.<br><br>
16351  * <p>
16352  * <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
16353  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16354  * <p>
16355  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16356  * source code that is used as the source inside a &lt;script> tag.<br><br>
16357  * <p>
16358  * In order for the browser to process the returned data, the server must wrap the data object
16359  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16360  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16361  * depending on whether the callback name was passed:
16362  * <p>
16363  * <pre><code>
16364 boolean scriptTag = false;
16365 String cb = request.getParameter("callback");
16366 if (cb != null) {
16367     scriptTag = true;
16368     response.setContentType("text/javascript");
16369 } else {
16370     response.setContentType("application/x-json");
16371 }
16372 Writer out = response.getWriter();
16373 if (scriptTag) {
16374     out.write(cb + "(");
16375 }
16376 out.print(dataBlock.toJsonString());
16377 if (scriptTag) {
16378     out.write(");");
16379 }
16380 </pre></code>
16381  *
16382  * @constructor
16383  * @param {Object} config A configuration object.
16384  */
16385 Roo.data.ScriptTagProxy = function(config){
16386     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16387     Roo.apply(this, config);
16388     this.head = document.getElementsByTagName("head")[0];
16389 };
16390
16391 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16392
16393 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16394     /**
16395      * @cfg {String} url The URL from which to request the data object.
16396      */
16397     /**
16398      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16399      */
16400     timeout : 30000,
16401     /**
16402      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16403      * the server the name of the callback function set up by the load call to process the returned data object.
16404      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16405      * javascript output which calls this named function passing the data object as its only parameter.
16406      */
16407     callbackParam : "callback",
16408     /**
16409      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16410      * name to the request.
16411      */
16412     nocache : true,
16413
16414     /**
16415      * Load data from the configured URL, read the data object into
16416      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16417      * process that block using the passed callback.
16418      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16419      * for the request to the remote server.
16420      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16421      * object into a block of Roo.data.Records.
16422      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16423      * The function must be passed <ul>
16424      * <li>The Record block object</li>
16425      * <li>The "arg" argument from the load function</li>
16426      * <li>A boolean success indicator</li>
16427      * </ul>
16428      * @param {Object} scope The scope in which to call the callback
16429      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16430      */
16431     load : function(params, reader, callback, scope, arg){
16432         if(this.fireEvent("beforeload", this, params) !== false){
16433
16434             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16435
16436             var url = this.url;
16437             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16438             if(this.nocache){
16439                 url += "&_dc=" + (new Date().getTime());
16440             }
16441             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16442             var trans = {
16443                 id : transId,
16444                 cb : "stcCallback"+transId,
16445                 scriptId : "stcScript"+transId,
16446                 params : params,
16447                 arg : arg,
16448                 url : url,
16449                 callback : callback,
16450                 scope : scope,
16451                 reader : reader
16452             };
16453             var conn = this;
16454
16455             window[trans.cb] = function(o){
16456                 conn.handleResponse(o, trans);
16457             };
16458
16459             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16460
16461             if(this.autoAbort !== false){
16462                 this.abort();
16463             }
16464
16465             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16466
16467             var script = document.createElement("script");
16468             script.setAttribute("src", url);
16469             script.setAttribute("type", "text/javascript");
16470             script.setAttribute("id", trans.scriptId);
16471             this.head.appendChild(script);
16472
16473             this.trans = trans;
16474         }else{
16475             callback.call(scope||this, null, arg, false);
16476         }
16477     },
16478
16479     // private
16480     isLoading : function(){
16481         return this.trans ? true : false;
16482     },
16483
16484     /**
16485      * Abort the current server request.
16486      */
16487     abort : function(){
16488         if(this.isLoading()){
16489             this.destroyTrans(this.trans);
16490         }
16491     },
16492
16493     // private
16494     destroyTrans : function(trans, isLoaded){
16495         this.head.removeChild(document.getElementById(trans.scriptId));
16496         clearTimeout(trans.timeoutId);
16497         if(isLoaded){
16498             window[trans.cb] = undefined;
16499             try{
16500                 delete window[trans.cb];
16501             }catch(e){}
16502         }else{
16503             // if hasn't been loaded, wait for load to remove it to prevent script error
16504             window[trans.cb] = function(){
16505                 window[trans.cb] = undefined;
16506                 try{
16507                     delete window[trans.cb];
16508                 }catch(e){}
16509             };
16510         }
16511     },
16512
16513     // private
16514     handleResponse : function(o, trans){
16515         this.trans = false;
16516         this.destroyTrans(trans, true);
16517         var result;
16518         try {
16519             result = trans.reader.readRecords(o);
16520         }catch(e){
16521             this.fireEvent("loadexception", this, o, trans.arg, e);
16522             trans.callback.call(trans.scope||window, null, trans.arg, false);
16523             return;
16524         }
16525         this.fireEvent("load", this, o, trans.arg);
16526         trans.callback.call(trans.scope||window, result, trans.arg, true);
16527     },
16528
16529     // private
16530     handleFailure : function(trans){
16531         this.trans = false;
16532         this.destroyTrans(trans, false);
16533         this.fireEvent("loadexception", this, null, trans.arg);
16534         trans.callback.call(trans.scope||window, null, trans.arg, false);
16535     }
16536 });/*
16537  * Based on:
16538  * Ext JS Library 1.1.1
16539  * Copyright(c) 2006-2007, Ext JS, LLC.
16540  *
16541  * Originally Released Under LGPL - original licence link has changed is not relivant.
16542  *
16543  * Fork - LGPL
16544  * <script type="text/javascript">
16545  */
16546
16547 /**
16548  * @class Roo.data.JsonReader
16549  * @extends Roo.data.DataReader
16550  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16551  * based on mappings in a provided Roo.data.Record constructor.
16552  * 
16553  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16554  * in the reply previously. 
16555  * 
16556  * <p>
16557  * Example code:
16558  * <pre><code>
16559 var RecordDef = Roo.data.Record.create([
16560     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16561     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16562 ]);
16563 var myReader = new Roo.data.JsonReader({
16564     totalProperty: "results",    // The property which contains the total dataset size (optional)
16565     root: "rows",                // The property which contains an Array of row objects
16566     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16567 }, RecordDef);
16568 </code></pre>
16569  * <p>
16570  * This would consume a JSON file like this:
16571  * <pre><code>
16572 { 'results': 2, 'rows': [
16573     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16574     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16575 }
16576 </code></pre>
16577  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16578  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16579  * paged from the remote server.
16580  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16581  * @cfg {String} root name of the property which contains the Array of row objects.
16582  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16583  * @cfg {Array} fields Array of field definition objects
16584  * @constructor
16585  * Create a new JsonReader
16586  * @param {Object} meta Metadata configuration options
16587  * @param {Object} recordType Either an Array of field definition objects,
16588  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16589  */
16590 Roo.data.JsonReader = function(meta, recordType){
16591     
16592     meta = meta || {};
16593     // set some defaults:
16594     Roo.applyIf(meta, {
16595         totalProperty: 'total',
16596         successProperty : 'success',
16597         root : 'data',
16598         id : 'id'
16599     });
16600     
16601     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16602 };
16603 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16604     
16605     readerType : 'Json',
16606     
16607     /**
16608      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16609      * Used by Store query builder to append _requestMeta to params.
16610      * 
16611      */
16612     metaFromRemote : false,
16613     /**
16614      * This method is only used by a DataProxy which has retrieved data from a remote server.
16615      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16616      * @return {Object} data A data block which is used by an Roo.data.Store object as
16617      * a cache of Roo.data.Records.
16618      */
16619     read : function(response){
16620         var json = response.responseText;
16621        
16622         var o = /* eval:var:o */ eval("("+json+")");
16623         if(!o) {
16624             throw {message: "JsonReader.read: Json object not found"};
16625         }
16626         
16627         if(o.metaData){
16628             
16629             delete this.ef;
16630             this.metaFromRemote = true;
16631             this.meta = o.metaData;
16632             this.recordType = Roo.data.Record.create(o.metaData.fields);
16633             this.onMetaChange(this.meta, this.recordType, o);
16634         }
16635         return this.readRecords(o);
16636     },
16637
16638     // private function a store will implement
16639     onMetaChange : function(meta, recordType, o){
16640
16641     },
16642
16643     /**
16644          * @ignore
16645          */
16646     simpleAccess: function(obj, subsc) {
16647         return obj[subsc];
16648     },
16649
16650         /**
16651          * @ignore
16652          */
16653     getJsonAccessor: function(){
16654         var re = /[\[\.]/;
16655         return function(expr) {
16656             try {
16657                 return(re.test(expr))
16658                     ? new Function("obj", "return obj." + expr)
16659                     : function(obj){
16660                         return obj[expr];
16661                     };
16662             } catch(e){}
16663             return Roo.emptyFn;
16664         };
16665     }(),
16666
16667     /**
16668      * Create a data block containing Roo.data.Records from an XML document.
16669      * @param {Object} o An object which contains an Array of row objects in the property specified
16670      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16671      * which contains the total size of the dataset.
16672      * @return {Object} data A data block which is used by an Roo.data.Store object as
16673      * a cache of Roo.data.Records.
16674      */
16675     readRecords : function(o){
16676         /**
16677          * After any data loads, the raw JSON data is available for further custom processing.
16678          * @type Object
16679          */
16680         this.o = o;
16681         var s = this.meta, Record = this.recordType,
16682             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16683
16684 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16685         if (!this.ef) {
16686             if(s.totalProperty) {
16687                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16688                 }
16689                 if(s.successProperty) {
16690                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16691                 }
16692                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16693                 if (s.id) {
16694                         var g = this.getJsonAccessor(s.id);
16695                         this.getId = function(rec) {
16696                                 var r = g(rec);  
16697                                 return (r === undefined || r === "") ? null : r;
16698                         };
16699                 } else {
16700                         this.getId = function(){return null;};
16701                 }
16702             this.ef = [];
16703             for(var jj = 0; jj < fl; jj++){
16704                 f = fi[jj];
16705                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16706                 this.ef[jj] = this.getJsonAccessor(map);
16707             }
16708         }
16709
16710         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16711         if(s.totalProperty){
16712             var vt = parseInt(this.getTotal(o), 10);
16713             if(!isNaN(vt)){
16714                 totalRecords = vt;
16715             }
16716         }
16717         if(s.successProperty){
16718             var vs = this.getSuccess(o);
16719             if(vs === false || vs === 'false'){
16720                 success = false;
16721             }
16722         }
16723         var records = [];
16724         for(var i = 0; i < c; i++){
16725             var n = root[i];
16726             var values = {};
16727             var id = this.getId(n);
16728             for(var j = 0; j < fl; j++){
16729                 f = fi[j];
16730                                 var v = this.ef[j](n);
16731                                 if (!f.convert) {
16732                                         Roo.log('missing convert for ' + f.name);
16733                                         Roo.log(f);
16734                                         continue;
16735                                 }
16736                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16737             }
16738                         if (!Record) {
16739                                 return {
16740                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16741                                         success : false,
16742                                         records : [],
16743                                         totalRecords : 0
16744                                 };
16745                         }
16746             var record = new Record(values, id);
16747             record.json = n;
16748             records[i] = record;
16749         }
16750         return {
16751             raw : o,
16752             success : success,
16753             records : records,
16754             totalRecords : totalRecords
16755         };
16756     },
16757     // used when loading children.. @see loadDataFromChildren
16758     toLoadData: function(rec)
16759     {
16760         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16761         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16762         return { data : data, total : data.length };
16763         
16764     }
16765 });/*
16766  * Based on:
16767  * Ext JS Library 1.1.1
16768  * Copyright(c) 2006-2007, Ext JS, LLC.
16769  *
16770  * Originally Released Under LGPL - original licence link has changed is not relivant.
16771  *
16772  * Fork - LGPL
16773  * <script type="text/javascript">
16774  */
16775
16776 /**
16777  * @class Roo.data.ArrayReader
16778  * @extends Roo.data.DataReader
16779  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16780  * Each element of that Array represents a row of data fields. The
16781  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16782  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16783  * <p>
16784  * Example code:.
16785  * <pre><code>
16786 var RecordDef = Roo.data.Record.create([
16787     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16788     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16789 ]);
16790 var myReader = new Roo.data.ArrayReader({
16791     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16792 }, RecordDef);
16793 </code></pre>
16794  * <p>
16795  * This would consume an Array like this:
16796  * <pre><code>
16797 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16798   </code></pre>
16799  
16800  * @constructor
16801  * Create a new JsonReader
16802  * @param {Object} meta Metadata configuration options.
16803  * @param {Object|Array} recordType Either an Array of field definition objects
16804  * 
16805  * @cfg {Array} fields Array of field definition objects
16806  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16807  * as specified to {@link Roo.data.Record#create},
16808  * or an {@link Roo.data.Record} object
16809  *
16810  * 
16811  * created using {@link Roo.data.Record#create}.
16812  */
16813 Roo.data.ArrayReader = function(meta, recordType)
16814 {    
16815     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16816 };
16817
16818 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16819     
16820       /**
16821      * Create a data block containing Roo.data.Records from an XML document.
16822      * @param {Object} o An Array of row objects which represents the dataset.
16823      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16824      * a cache of Roo.data.Records.
16825      */
16826     readRecords : function(o)
16827     {
16828         var sid = this.meta ? this.meta.id : null;
16829         var recordType = this.recordType, fields = recordType.prototype.fields;
16830         var records = [];
16831         var root = o;
16832         for(var i = 0; i < root.length; i++){
16833             var n = root[i];
16834             var values = {};
16835             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16836             for(var j = 0, jlen = fields.length; j < jlen; j++){
16837                 var f = fields.items[j];
16838                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16839                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16840                 v = f.convert(v);
16841                 values[f.name] = v;
16842             }
16843             var record = new recordType(values, id);
16844             record.json = n;
16845             records[records.length] = record;
16846         }
16847         return {
16848             records : records,
16849             totalRecords : records.length
16850         };
16851     },
16852     // used when loading children.. @see loadDataFromChildren
16853     toLoadData: function(rec)
16854     {
16855         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16856         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16857         
16858     }
16859     
16860     
16861 });/*
16862  * - LGPL
16863  * * 
16864  */
16865
16866 /**
16867  * @class Roo.bootstrap.form.ComboBox
16868  * @extends Roo.bootstrap.form.TriggerField
16869  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16870  * @cfg {Boolean} append (true|false) default false
16871  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16872  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16873  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16874  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16875  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16876  * @cfg {Boolean} animate default true
16877  * @cfg {Boolean} emptyResultText only for touch device
16878  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16879  * @cfg {String} emptyTitle default ''
16880  * @cfg {Number} width fixed with? experimental
16881  * @constructor
16882  * Create a new ComboBox.
16883  * @param {Object} config Configuration options
16884  */
16885 Roo.bootstrap.form.ComboBox = function(config){
16886     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16887     this.addEvents({
16888         /**
16889          * @event expand
16890          * Fires when the dropdown list is expanded
16891         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16892         */
16893         'expand' : true,
16894         /**
16895          * @event collapse
16896          * Fires when the dropdown list is collapsed
16897         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16898         */
16899         'collapse' : true,
16900         /**
16901          * @event beforeselect
16902          * Fires before a list item is selected. Return false to cancel the selection.
16903         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16904         * @param {Roo.data.Record} record The data record returned from the underlying store
16905         * @param {Number} index The index of the selected item in the dropdown list
16906         */
16907         'beforeselect' : true,
16908         /**
16909          * @event select
16910          * Fires when a list item is selected
16911         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16912         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16913         * @param {Number} index The index of the selected item in the dropdown list
16914         */
16915         'select' : true,
16916         /**
16917          * @event beforequery
16918          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16919          * The event object passed has these properties:
16920         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16921         * @param {String} query The query
16922         * @param {Boolean} forceAll true to force "all" query
16923         * @param {Boolean} cancel true to cancel the query
16924         * @param {Object} e The query event object
16925         */
16926         'beforequery': true,
16927          /**
16928          * @event add
16929          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16930         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16931         */
16932         'add' : true,
16933         /**
16934          * @event edit
16935          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16936         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16937         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16938         */
16939         'edit' : true,
16940         /**
16941          * @event remove
16942          * Fires when the remove value from the combobox array
16943         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16944         */
16945         'remove' : true,
16946         /**
16947          * @event afterremove
16948          * Fires when the remove value from the combobox array
16949         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16950         */
16951         'afterremove' : true,
16952         /**
16953          * @event specialfilter
16954          * Fires when specialfilter
16955             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16956             */
16957         'specialfilter' : true,
16958         /**
16959          * @event tick
16960          * Fires when tick the element
16961             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16962             */
16963         'tick' : true,
16964         /**
16965          * @event touchviewdisplay
16966          * Fires when touch view require special display (default is using displayField)
16967             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16968             * @param {Object} cfg set html .
16969             */
16970         'touchviewdisplay' : true
16971         
16972     });
16973     
16974     this.item = [];
16975     this.tickItems = [];
16976     
16977     this.selectedIndex = -1;
16978     if(this.mode == 'local'){
16979         if(config.queryDelay === undefined){
16980             this.queryDelay = 10;
16981         }
16982         if(config.minChars === undefined){
16983             this.minChars = 0;
16984         }
16985     }
16986 };
16987
16988 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16989      
16990     /**
16991      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16992      * rendering into an Roo.Editor, defaults to false)
16993      */
16994     /**
16995      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16996      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16997      */
16998     /**
16999      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
17000      */
17001     /**
17002      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
17003      * the dropdown list (defaults to undefined, with no header element)
17004      */
17005
17006      /**
17007      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
17008      */
17009      
17010      /**
17011      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
17012      */
17013     listWidth: undefined,
17014     /**
17015      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
17016      * mode = 'remote' or 'text' if mode = 'local')
17017      */
17018     displayField: undefined,
17019     
17020     /**
17021      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
17022      * mode = 'remote' or 'value' if mode = 'local'). 
17023      * Note: use of a valueField requires the user make a selection
17024      * in order for a value to be mapped.
17025      */
17026     valueField: undefined,
17027     /**
17028      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17029      */
17030     modalTitle : '',
17031     
17032     /**
17033      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17034      * field's data value (defaults to the underlying DOM element's name)
17035      */
17036     hiddenName: undefined,
17037     /**
17038      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17039      */
17040     listClass: '',
17041     /**
17042      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17043      */
17044     selectedClass: 'active',
17045     
17046     /**
17047      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17048      */
17049     shadow:'sides',
17050     /**
17051      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17052      * anchor positions (defaults to 'tl-bl')
17053      */
17054     listAlign: 'tl-bl?',
17055     /**
17056      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17057      */
17058     maxHeight: 300,
17059     /**
17060      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17061      * query specified by the allQuery config option (defaults to 'query')
17062      */
17063     triggerAction: 'query',
17064     /**
17065      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17066      * (defaults to 4, does not apply if editable = false)
17067      */
17068     minChars : 4,
17069     /**
17070      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17071      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17072      */
17073     typeAhead: false,
17074     /**
17075      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17076      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17077      */
17078     queryDelay: 500,
17079     /**
17080      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17081      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17082      */
17083     pageSize: 0,
17084     /**
17085      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17086      * when editable = true (defaults to false)
17087      */
17088     selectOnFocus:false,
17089     /**
17090      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17091      */
17092     queryParam: 'query',
17093     /**
17094      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17095      * when mode = 'remote' (defaults to 'Loading...')
17096      */
17097     loadingText: 'Loading...',
17098     /**
17099      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17100      */
17101     resizable: false,
17102     /**
17103      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17104      */
17105     handleHeight : 8,
17106     /**
17107      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17108      * traditional select (defaults to true)
17109      */
17110     editable: true,
17111     /**
17112      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17113      */
17114     allQuery: '',
17115     /**
17116      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17117      */
17118     mode: 'remote',
17119     /**
17120      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17121      * listWidth has a higher value)
17122      */
17123     minListWidth : 70,
17124     /**
17125      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17126      * allow the user to set arbitrary text into the field (defaults to false)
17127      */
17128     forceSelection:false,
17129     /**
17130      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17131      * if typeAhead = true (defaults to 250)
17132      */
17133     typeAheadDelay : 250,
17134     /**
17135      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17136      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17137      */
17138     valueNotFoundText : undefined,
17139     /**
17140      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17141      */
17142     blockFocus : false,
17143     
17144     /**
17145      * @cfg {Boolean} disableClear Disable showing of clear button.
17146      */
17147     disableClear : false,
17148     /**
17149      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17150      */
17151     alwaysQuery : false,
17152     
17153     /**
17154      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17155      */
17156     multiple : false,
17157     
17158     /**
17159      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17160      */
17161     invalidClass : "has-warning",
17162     
17163     /**
17164      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17165      */
17166     validClass : "has-success",
17167     
17168     /**
17169      * @cfg {Boolean} specialFilter (true|false) special filter default false
17170      */
17171     specialFilter : false,
17172     
17173     /**
17174      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17175      */
17176     mobileTouchView : true,
17177     
17178     /**
17179      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17180      */
17181     useNativeIOS : false,
17182     
17183     /**
17184      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17185      */
17186     mobile_restrict_height : false,
17187     
17188     ios_options : false,
17189     
17190     //private
17191     addicon : false,
17192     editicon: false,
17193     
17194     page: 0,
17195     hasQuery: false,
17196     append: false,
17197     loadNext: false,
17198     autoFocus : true,
17199     tickable : false,
17200     btnPosition : 'right',
17201     triggerList : true,
17202     showToggleBtn : true,
17203     animate : true,
17204     emptyResultText: 'Empty',
17205     triggerText : 'Select',
17206     emptyTitle : '',
17207     width : false,
17208     
17209     // element that contains real text value.. (when hidden is used..)
17210     
17211     getAutoCreate : function()
17212     {   
17213         var cfg = false;
17214         //render
17215         /*
17216          * Render classic select for iso
17217          */
17218         
17219         if(Roo.isIOS && this.useNativeIOS){
17220             cfg = this.getAutoCreateNativeIOS();
17221             return cfg;
17222         }
17223         
17224         /*
17225          * Touch Devices
17226          */
17227         
17228         if(Roo.isTouch && this.mobileTouchView){
17229             cfg = this.getAutoCreateTouchView();
17230             return cfg;;
17231         }
17232         
17233         /*
17234          *  Normal ComboBox
17235          */
17236         if(!this.tickable){
17237             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17238             return cfg;
17239         }
17240         
17241         /*
17242          *  ComboBox with tickable selections
17243          */
17244              
17245         var align = this.labelAlign || this.parentLabelAlign();
17246         
17247         cfg = {
17248             cls : 'form-group roo-combobox-tickable' //input-group
17249         };
17250         
17251         var btn_text_select = '';
17252         var btn_text_done = '';
17253         var btn_text_cancel = '';
17254         
17255         if (this.btn_text_show) {
17256             btn_text_select = 'Select';
17257             btn_text_done = 'Done';
17258             btn_text_cancel = 'Cancel'; 
17259         }
17260         
17261         var buttons = {
17262             tag : 'div',
17263             cls : 'tickable-buttons',
17264             cn : [
17265                 {
17266                     tag : 'button',
17267                     type : 'button',
17268                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17269                     //html : this.triggerText
17270                     html: btn_text_select
17271                 },
17272                 {
17273                     tag : 'button',
17274                     type : 'button',
17275                     name : 'ok',
17276                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17277                     //html : 'Done'
17278                     html: btn_text_done
17279                 },
17280                 {
17281                     tag : 'button',
17282                     type : 'button',
17283                     name : 'cancel',
17284                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17285                     //html : 'Cancel'
17286                     html: btn_text_cancel
17287                 }
17288             ]
17289         };
17290         
17291         if(this.editable){
17292             buttons.cn.unshift({
17293                 tag: 'input',
17294                 cls: 'roo-select2-search-field-input'
17295             });
17296         }
17297         
17298         var _this = this;
17299         
17300         Roo.each(buttons.cn, function(c){
17301             if (_this.size) {
17302                 c.cls += ' btn-' + _this.size;
17303             }
17304
17305             if (_this.disabled) {
17306                 c.disabled = true;
17307             }
17308         });
17309         
17310         var box = {
17311             tag: 'div',
17312             style : 'display: contents',
17313             cn: [
17314                 {
17315                     tag: 'input',
17316                     type : 'hidden',
17317                     cls: 'form-hidden-field'
17318                 },
17319                 {
17320                     tag: 'ul',
17321                     cls: 'roo-select2-choices',
17322                     cn:[
17323                         {
17324                             tag: 'li',
17325                             cls: 'roo-select2-search-field',
17326                             cn: [
17327                                 buttons
17328                             ]
17329                         }
17330                     ]
17331                 }
17332             ]
17333         };
17334         
17335         var combobox = {
17336             cls: 'roo-select2-container input-group roo-select2-container-multi',
17337             cn: [
17338                 
17339                 box
17340 //                {
17341 //                    tag: 'ul',
17342 //                    cls: 'typeahead typeahead-long dropdown-menu',
17343 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17344 //                }
17345             ]
17346         };
17347         
17348         if(this.hasFeedback && !this.allowBlank){
17349             
17350             var feedback = {
17351                 tag: 'span',
17352                 cls: 'glyphicon form-control-feedback'
17353             };
17354
17355             combobox.cn.push(feedback);
17356         }
17357         
17358         
17359         
17360         var indicator = {
17361             tag : 'i',
17362             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17363             tooltip : 'This field is required'
17364         };
17365         if (Roo.bootstrap.version == 4) {
17366             indicator = {
17367                 tag : 'i',
17368                 style : 'display:none'
17369             };
17370         }
17371         if (align ==='left' && this.fieldLabel.length) {
17372             
17373             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17374             
17375             cfg.cn = [
17376                 indicator,
17377                 {
17378                     tag: 'label',
17379                     'for' :  id,
17380                     cls : 'control-label col-form-label',
17381                     html : this.fieldLabel
17382
17383                 },
17384                 {
17385                     cls : "", 
17386                     cn: [
17387                         combobox
17388                     ]
17389                 }
17390
17391             ];
17392             
17393             var labelCfg = cfg.cn[1];
17394             var contentCfg = cfg.cn[2];
17395             
17396
17397             if(this.indicatorpos == 'right'){
17398                 
17399                 cfg.cn = [
17400                     {
17401                         tag: 'label',
17402                         'for' :  id,
17403                         cls : 'control-label col-form-label',
17404                         cn : [
17405                             {
17406                                 tag : 'span',
17407                                 html : this.fieldLabel
17408                             },
17409                             indicator
17410                         ]
17411                     },
17412                     {
17413                         cls : "",
17414                         cn: [
17415                             combobox
17416                         ]
17417                     }
17418
17419                 ];
17420                 
17421                 
17422                 
17423                 labelCfg = cfg.cn[0];
17424                 contentCfg = cfg.cn[1];
17425             
17426             }
17427             
17428             if(this.labelWidth > 12){
17429                 labelCfg.style = "width: " + this.labelWidth + 'px';
17430             }
17431             if(this.width * 1 > 0){
17432                 contentCfg.style = "width: " + this.width + 'px';
17433             }
17434             if(this.labelWidth < 13 && this.labelmd == 0){
17435                 this.labelmd = this.labelWidth;
17436             }
17437             
17438             if(this.labellg > 0){
17439                 labelCfg.cls += ' col-lg-' + this.labellg;
17440                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17441             }
17442             
17443             if(this.labelmd > 0){
17444                 labelCfg.cls += ' col-md-' + this.labelmd;
17445                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17446             }
17447             
17448             if(this.labelsm > 0){
17449                 labelCfg.cls += ' col-sm-' + this.labelsm;
17450                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17451             }
17452             
17453             if(this.labelxs > 0){
17454                 labelCfg.cls += ' col-xs-' + this.labelxs;
17455                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17456             }
17457                 
17458                 
17459         } else if ( this.fieldLabel.length) {
17460 //                Roo.log(" label");
17461                  cfg.cn = [
17462                    indicator,
17463                     {
17464                         tag: 'label',
17465                         //cls : 'input-group-addon',
17466                         html : this.fieldLabel
17467                     },
17468                     combobox
17469                 ];
17470                 
17471                 if(this.indicatorpos == 'right'){
17472                     cfg.cn = [
17473                         {
17474                             tag: 'label',
17475                             //cls : 'input-group-addon',
17476                             html : this.fieldLabel
17477                         },
17478                         indicator,
17479                         combobox
17480                     ];
17481                     
17482                 }
17483
17484         } else {
17485             
17486 //                Roo.log(" no label && no align");
17487                 cfg = combobox
17488                      
17489                 
17490         }
17491          
17492         var settings=this;
17493         ['xs','sm','md','lg'].map(function(size){
17494             if (settings[size]) {
17495                 cfg.cls += ' col-' + size + '-' + settings[size];
17496             }
17497         });
17498         
17499         return cfg;
17500         
17501     },
17502     
17503     _initEventsCalled : false,
17504     
17505     // private
17506     initEvents: function()
17507     {   
17508         if (this._initEventsCalled) { // as we call render... prevent looping...
17509             return;
17510         }
17511         this._initEventsCalled = true;
17512         
17513         if (!this.store) {
17514             throw "can not find store for combo";
17515         }
17516         
17517         this.indicator = this.indicatorEl();
17518         
17519         this.store = Roo.factory(this.store, Roo.data);
17520         this.store.parent = this;
17521         
17522         // if we are building from html. then this element is so complex, that we can not really
17523         // use the rendered HTML.
17524         // so we have to trash and replace the previous code.
17525         if (Roo.XComponent.build_from_html) {
17526             // remove this element....
17527             var e = this.el.dom, k=0;
17528             while (e ) { e = e.previousSibling;  ++k;}
17529
17530             this.el.remove();
17531             
17532             this.el=false;
17533             this.rendered = false;
17534             
17535             this.render(this.parent().getChildContainer(true), k);
17536         }
17537         
17538         if(Roo.isIOS && this.useNativeIOS){
17539             this.initIOSView();
17540             return;
17541         }
17542         
17543         /*
17544          * Touch Devices
17545          */
17546         
17547         if(Roo.isTouch && this.mobileTouchView){
17548             this.initTouchView();
17549             return;
17550         }
17551         
17552         if(this.tickable){
17553             this.initTickableEvents();
17554             return;
17555         }
17556         
17557         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17558         
17559         if(this.hiddenName){
17560             
17561             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17562             
17563             this.hiddenField.dom.value =
17564                 this.hiddenValue !== undefined ? this.hiddenValue :
17565                 this.value !== undefined ? this.value : '';
17566
17567             // prevent input submission
17568             this.el.dom.removeAttribute('name');
17569             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17570              
17571              
17572         }
17573         //if(Roo.isGecko){
17574         //    this.el.dom.setAttribute('autocomplete', 'off');
17575         //}
17576         
17577         var cls = 'x-combo-list';
17578         
17579         //this.list = new Roo.Layer({
17580         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17581         //});
17582         
17583         var _this = this;
17584         
17585         (function(){
17586             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17587             _this.list.setWidth(lw);
17588         }).defer(100);
17589         
17590         this.list.on('mouseover', this.onViewOver, this);
17591         this.list.on('mousemove', this.onViewMove, this);
17592         this.list.on('scroll', this.onViewScroll, this);
17593         
17594         /*
17595         this.list.swallowEvent('mousewheel');
17596         this.assetHeight = 0;
17597
17598         if(this.title){
17599             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17600             this.assetHeight += this.header.getHeight();
17601         }
17602
17603         this.innerList = this.list.createChild({cls:cls+'-inner'});
17604         this.innerList.on('mouseover', this.onViewOver, this);
17605         this.innerList.on('mousemove', this.onViewMove, this);
17606         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17607         
17608         if(this.allowBlank && !this.pageSize && !this.disableClear){
17609             this.footer = this.list.createChild({cls:cls+'-ft'});
17610             this.pageTb = new Roo.Toolbar(this.footer);
17611            
17612         }
17613         if(this.pageSize){
17614             this.footer = this.list.createChild({cls:cls+'-ft'});
17615             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17616                     {pageSize: this.pageSize});
17617             
17618         }
17619         
17620         if (this.pageTb && this.allowBlank && !this.disableClear) {
17621             var _this = this;
17622             this.pageTb.add(new Roo.Toolbar.Fill(), {
17623                 cls: 'x-btn-icon x-btn-clear',
17624                 text: '&#160;',
17625                 handler: function()
17626                 {
17627                     _this.collapse();
17628                     _this.clearValue();
17629                     _this.onSelect(false, -1);
17630                 }
17631             });
17632         }
17633         if (this.footer) {
17634             this.assetHeight += this.footer.getHeight();
17635         }
17636         */
17637             
17638         if(!this.tpl){
17639             this.tpl = Roo.bootstrap.version == 4 ?
17640                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17641                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17642         }
17643
17644         this.view = new Roo.View(this.list, this.tpl, {
17645             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17646         });
17647         //this.view.wrapEl.setDisplayed(false);
17648         this.view.on('click', this.onViewClick, this);
17649         
17650         
17651         this.store.on('beforeload', this.onBeforeLoad, this);
17652         this.store.on('load', this.onLoad, this);
17653         this.store.on('loadexception', this.onLoadException, this);
17654         /*
17655         if(this.resizable){
17656             this.resizer = new Roo.Resizable(this.list,  {
17657                pinned:true, handles:'se'
17658             });
17659             this.resizer.on('resize', function(r, w, h){
17660                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17661                 this.listWidth = w;
17662                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17663                 this.restrictHeight();
17664             }, this);
17665             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17666         }
17667         */
17668         if(!this.editable){
17669             this.editable = true;
17670             this.setEditable(false);
17671         }
17672         
17673         /*
17674         
17675         if (typeof(this.events.add.listeners) != 'undefined') {
17676             
17677             this.addicon = this.wrap.createChild(
17678                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17679        
17680             this.addicon.on('click', function(e) {
17681                 this.fireEvent('add', this);
17682             }, this);
17683         }
17684         if (typeof(this.events.edit.listeners) != 'undefined') {
17685             
17686             this.editicon = this.wrap.createChild(
17687                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17688             if (this.addicon) {
17689                 this.editicon.setStyle('margin-left', '40px');
17690             }
17691             this.editicon.on('click', function(e) {
17692                 
17693                 // we fire even  if inothing is selected..
17694                 this.fireEvent('edit', this, this.lastData );
17695                 
17696             }, this);
17697         }
17698         */
17699         
17700         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17701             "up" : function(e){
17702                 this.inKeyMode = true;
17703                 this.selectPrev();
17704             },
17705
17706             "down" : function(e){
17707                 if(!this.isExpanded()){
17708                     this.onTriggerClick();
17709                 }else{
17710                     this.inKeyMode = true;
17711                     this.selectNext();
17712                 }
17713             },
17714
17715             "enter" : function(e){
17716 //                this.onViewClick();
17717                 //return true;
17718                 this.collapse();
17719                 
17720                 if(this.fireEvent("specialkey", this, e)){
17721                     this.onViewClick(false);
17722                 }
17723                 
17724                 return true;
17725             },
17726
17727             "esc" : function(e){
17728                 this.collapse();
17729             },
17730
17731             "tab" : function(e){
17732                 this.collapse();
17733                 
17734                 if(this.fireEvent("specialkey", this, e)){
17735                     this.onViewClick(false);
17736                 }
17737                 
17738                 return true;
17739             },
17740
17741             scope : this,
17742
17743             doRelay : function(foo, bar, hname){
17744                 if(hname == 'down' || this.scope.isExpanded()){
17745                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17746                 }
17747                 return true;
17748             },
17749
17750             forceKeyDown: true
17751         });
17752         
17753         
17754         this.queryDelay = Math.max(this.queryDelay || 10,
17755                 this.mode == 'local' ? 10 : 250);
17756         
17757         
17758         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17759         
17760         if(this.typeAhead){
17761             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17762         }
17763         if(this.editable !== false){
17764             this.inputEl().on("keyup", this.onKeyUp, this);
17765         }
17766         if(this.forceSelection){
17767             this.inputEl().on('blur', this.doForce, this);
17768         }
17769         
17770         if(this.multiple){
17771             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17772             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17773         }
17774     },
17775     
17776     initTickableEvents: function()
17777     {   
17778         this.createList();
17779         
17780         if(this.hiddenName){
17781             
17782             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17783             
17784             this.hiddenField.dom.value =
17785                 this.hiddenValue !== undefined ? this.hiddenValue :
17786                 this.value !== undefined ? this.value : '';
17787
17788             // prevent input submission
17789             this.el.dom.removeAttribute('name');
17790             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17791              
17792              
17793         }
17794         
17795 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17796         
17797         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17798         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17799         if(this.triggerList){
17800             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17801         }
17802          
17803         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17804         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17805         
17806         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17807         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17808         
17809         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17810         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17811         
17812         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17813         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17814         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17815         
17816         this.okBtn.hide();
17817         this.cancelBtn.hide();
17818         
17819         var _this = this;
17820         
17821         (function(){
17822             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17823             _this.list.setWidth(lw);
17824         }).defer(100);
17825         
17826         this.list.on('mouseover', this.onViewOver, this);
17827         this.list.on('mousemove', this.onViewMove, this);
17828         
17829         this.list.on('scroll', this.onViewScroll, this);
17830         
17831         if(!this.tpl){
17832             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17833                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17834         }
17835
17836         this.view = new Roo.View(this.list, this.tpl, {
17837             singleSelect:true,
17838             tickable:true,
17839             parent:this,
17840             store: this.store,
17841             selectedClass: this.selectedClass
17842         });
17843         
17844         //this.view.wrapEl.setDisplayed(false);
17845         this.view.on('click', this.onViewClick, this);
17846         
17847         
17848         
17849         this.store.on('beforeload', this.onBeforeLoad, this);
17850         this.store.on('load', this.onLoad, this);
17851         this.store.on('loadexception', this.onLoadException, this);
17852         
17853         if(this.editable){
17854             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17855                 "up" : function(e){
17856                     this.inKeyMode = true;
17857                     this.selectPrev();
17858                 },
17859
17860                 "down" : function(e){
17861                     this.inKeyMode = true;
17862                     this.selectNext();
17863                 },
17864
17865                 "enter" : function(e){
17866                     if(this.fireEvent("specialkey", this, e)){
17867                         this.onViewClick(false);
17868                     }
17869                     
17870                     return true;
17871                 },
17872
17873                 "esc" : function(e){
17874                     this.onTickableFooterButtonClick(e, false, false);
17875                 },
17876
17877                 "tab" : function(e){
17878                     this.fireEvent("specialkey", this, e);
17879                     
17880                     this.onTickableFooterButtonClick(e, false, false);
17881                     
17882                     return true;
17883                 },
17884
17885                 scope : this,
17886
17887                 doRelay : function(e, fn, key){
17888                     if(this.scope.isExpanded()){
17889                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17890                     }
17891                     return true;
17892                 },
17893
17894                 forceKeyDown: true
17895             });
17896         }
17897         
17898         this.queryDelay = Math.max(this.queryDelay || 10,
17899                 this.mode == 'local' ? 10 : 250);
17900         
17901         
17902         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17903         
17904         if(this.typeAhead){
17905             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17906         }
17907         
17908         if(this.editable !== false){
17909             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17910         }
17911         
17912         this.indicator = this.indicatorEl();
17913         
17914         if(this.indicator){
17915             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17916             this.indicator.hide();
17917         }
17918         
17919     },
17920
17921     onDestroy : function(){
17922         if(this.view){
17923             this.view.setStore(null);
17924             this.view.el.removeAllListeners();
17925             this.view.el.remove();
17926             this.view.purgeListeners();
17927         }
17928         if(this.list){
17929             this.list.dom.innerHTML  = '';
17930         }
17931         
17932         if(this.store){
17933             this.store.un('beforeload', this.onBeforeLoad, this);
17934             this.store.un('load', this.onLoad, this);
17935             this.store.un('loadexception', this.onLoadException, this);
17936         }
17937         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17938     },
17939
17940     // private
17941     fireKey : function(e){
17942         if(e.isNavKeyPress() && !this.list.isVisible()){
17943             this.fireEvent("specialkey", this, e);
17944         }
17945     },
17946
17947     // private
17948     onResize: function(w, h)
17949     {
17950         
17951         
17952 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17953 //        
17954 //        if(typeof w != 'number'){
17955 //            // we do not handle it!?!?
17956 //            return;
17957 //        }
17958 //        var tw = this.trigger.getWidth();
17959 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17960 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17961 //        var x = w - tw;
17962 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17963 //            
17964 //        //this.trigger.setStyle('left', x+'px');
17965 //        
17966 //        if(this.list && this.listWidth === undefined){
17967 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17968 //            this.list.setWidth(lw);
17969 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17970 //        }
17971         
17972     
17973         
17974     },
17975
17976     /**
17977      * Allow or prevent the user from directly editing the field text.  If false is passed,
17978      * the user will only be able to select from the items defined in the dropdown list.  This method
17979      * is the runtime equivalent of setting the 'editable' config option at config time.
17980      * @param {Boolean} value True to allow the user to directly edit the field text
17981      */
17982     setEditable : function(value){
17983         if(value == this.editable){
17984             return;
17985         }
17986         this.editable = value;
17987         if(!value){
17988             this.inputEl().dom.setAttribute('readOnly', true);
17989             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17990             this.inputEl().addClass('x-combo-noedit');
17991         }else{
17992             this.inputEl().dom.removeAttribute('readOnly');
17993             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17994             this.inputEl().removeClass('x-combo-noedit');
17995         }
17996     },
17997
17998     // private
17999     
18000     onBeforeLoad : function(combo,opts){
18001         if(!this.hasFocus){
18002             return;
18003         }
18004          if (!opts.add) {
18005             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
18006          }
18007         this.restrictHeight();
18008         this.selectedIndex = -1;
18009     },
18010
18011     // private
18012     onLoad : function(){
18013         
18014         this.hasQuery = false;
18015         
18016         if(!this.hasFocus){
18017             return;
18018         }
18019         
18020         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18021             this.loading.hide();
18022         }
18023         
18024         if(this.store.getCount() > 0){
18025             
18026             this.expand();
18027             this.restrictHeight();
18028             if(this.lastQuery == this.allQuery){
18029                 if(this.editable && !this.tickable){
18030                     this.inputEl().dom.select();
18031                 }
18032                 
18033                 if(
18034                     !this.selectByValue(this.value, true) &&
18035                     this.autoFocus && 
18036                     (
18037                         !this.store.lastOptions ||
18038                         typeof(this.store.lastOptions.add) == 'undefined' || 
18039                         this.store.lastOptions.add != true
18040                     )
18041                 ){
18042                     this.select(0, true);
18043                 }
18044             }else{
18045                 if(this.autoFocus){
18046                     this.selectNext();
18047                 }
18048                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18049                     this.taTask.delay(this.typeAheadDelay);
18050                 }
18051             }
18052         }else{
18053             this.onEmptyResults();
18054         }
18055         
18056         //this.el.focus();
18057     },
18058     // private
18059     onLoadException : function()
18060     {
18061         this.hasQuery = false;
18062         
18063         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18064             this.loading.hide();
18065         }
18066         
18067         if(this.tickable && this.editable){
18068             return;
18069         }
18070         
18071         this.collapse();
18072         // only causes errors at present
18073         //Roo.log(this.store.reader.jsonData);
18074         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18075             // fixme
18076             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18077         //}
18078         
18079         
18080     },
18081     // private
18082     onTypeAhead : function(){
18083         if(this.store.getCount() > 0){
18084             var r = this.store.getAt(0);
18085             var newValue = r.data[this.displayField];
18086             var len = newValue.length;
18087             var selStart = this.getRawValue().length;
18088             
18089             if(selStart != len){
18090                 this.setRawValue(newValue);
18091                 this.selectText(selStart, newValue.length);
18092             }
18093         }
18094     },
18095
18096     // private
18097     onSelect : function(record, index){
18098         
18099         if(this.fireEvent('beforeselect', this, record, index) !== false){
18100         
18101             this.setFromData(index > -1 ? record.data : false);
18102             
18103             this.collapse();
18104             this.fireEvent('select', this, record, index);
18105         }
18106     },
18107
18108     /**
18109      * Returns the currently selected field value or empty string if no value is set.
18110      * @return {String} value The selected value
18111      */
18112     getValue : function()
18113     {
18114         if(Roo.isIOS && this.useNativeIOS){
18115             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18116         }
18117         
18118         if(this.multiple){
18119             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18120         }
18121         
18122         if(this.valueField){
18123             return typeof this.value != 'undefined' ? this.value : '';
18124         }else{
18125             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18126         }
18127     },
18128     
18129     getRawValue : function()
18130     {
18131         if(Roo.isIOS && this.useNativeIOS){
18132             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18133         }
18134         
18135         var v = this.inputEl().getValue();
18136         
18137         return v;
18138     },
18139
18140     /**
18141      * Clears any text/value currently set in the field
18142      */
18143     clearValue : function(){
18144         
18145         if(this.hiddenField){
18146             this.hiddenField.dom.value = '';
18147         }
18148         this.value = '';
18149         this.setRawValue('');
18150         this.lastSelectionText = '';
18151         this.lastData = false;
18152         
18153         var close = this.closeTriggerEl();
18154         
18155         if(close){
18156             close.hide();
18157         }
18158         
18159         this.validate();
18160         
18161     },
18162
18163     /**
18164      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18165      * will be displayed in the field.  If the value does not match the data value of an existing item,
18166      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18167      * Otherwise the field will be blank (although the value will still be set).
18168      * @param {String} value The value to match
18169      */
18170     setValue : function(v)
18171     {
18172         if(Roo.isIOS && this.useNativeIOS){
18173             this.setIOSValue(v);
18174             return;
18175         }
18176         
18177         if(this.multiple){
18178             this.syncValue();
18179             return;
18180         }
18181         
18182         var text = v;
18183         if(this.valueField){
18184             var r = this.findRecord(this.valueField, v);
18185             if(r){
18186                 text = r.data[this.displayField];
18187             }else if(this.valueNotFoundText !== undefined){
18188                 text = this.valueNotFoundText;
18189             }
18190         }
18191         this.lastSelectionText = text;
18192         if(this.hiddenField){
18193             this.hiddenField.dom.value = v;
18194         }
18195         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18196         this.value = v;
18197         
18198         var close = this.closeTriggerEl();
18199         
18200         if(close){
18201             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18202         }
18203         
18204         this.validate();
18205     },
18206     /**
18207      * @property {Object} the last set data for the element
18208      */
18209     
18210     lastData : false,
18211     /**
18212      * Sets the value of the field based on a object which is related to the record format for the store.
18213      * @param {Object} value the value to set as. or false on reset?
18214      */
18215     setFromData : function(o){
18216         
18217         if(this.multiple){
18218             this.addItem(o);
18219             return;
18220         }
18221             
18222         var dv = ''; // display value
18223         var vv = ''; // value value..
18224         this.lastData = o;
18225         if (this.displayField) {
18226             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18227         } else {
18228             // this is an error condition!!!
18229             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18230         }
18231         
18232         if(this.valueField){
18233             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18234         }
18235         
18236         var close = this.closeTriggerEl();
18237         
18238         if(close){
18239             if(dv.length || vv * 1 > 0){
18240                 close.show() ;
18241                 this.blockFocus=true;
18242             } else {
18243                 close.hide();
18244             }             
18245         }
18246         
18247         if(this.hiddenField){
18248             this.hiddenField.dom.value = vv;
18249             
18250             this.lastSelectionText = dv;
18251             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18252             this.value = vv;
18253             return;
18254         }
18255         // no hidden field.. - we store the value in 'value', but still display
18256         // display field!!!!
18257         this.lastSelectionText = dv;
18258         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18259         this.value = vv;
18260         
18261         
18262         
18263     },
18264     // private
18265     reset : function(){
18266         // overridden so that last data is reset..
18267         
18268         if(this.multiple){
18269             this.clearItem();
18270             return;
18271         }
18272         
18273         this.setValue(this.originalValue);
18274         //this.clearInvalid();
18275         this.lastData = false;
18276         if (this.view) {
18277             this.view.clearSelections();
18278         }
18279         
18280         this.validate();
18281     },
18282     // private
18283     findRecord : function(prop, value){
18284         var record;
18285         if(this.store.getCount() > 0){
18286             this.store.each(function(r){
18287                 if(r.data[prop] == value){
18288                     record = r;
18289                     return false;
18290                 }
18291                 return true;
18292             });
18293         }
18294         return record;
18295     },
18296     
18297     getName: function()
18298     {
18299         // returns hidden if it's set..
18300         if (!this.rendered) {return ''};
18301         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18302         
18303     },
18304     // private
18305     onViewMove : function(e, t){
18306         this.inKeyMode = false;
18307     },
18308
18309     // private
18310     onViewOver : function(e, t){
18311         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18312             return;
18313         }
18314         var item = this.view.findItemFromChild(t);
18315         
18316         if(item){
18317             var index = this.view.indexOf(item);
18318             this.select(index, false);
18319         }
18320     },
18321
18322     // private
18323     onViewClick : function(view, doFocus, el, e)
18324     {
18325         var index = this.view.getSelectedIndexes()[0];
18326         
18327         var r = this.store.getAt(index);
18328         
18329         if(this.tickable){
18330             
18331             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18332                 return;
18333             }
18334             
18335             var rm = false;
18336             var _this = this;
18337             
18338             Roo.each(this.tickItems, function(v,k){
18339                 
18340                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18341                     Roo.log(v);
18342                     _this.tickItems.splice(k, 1);
18343                     
18344                     if(typeof(e) == 'undefined' && view == false){
18345                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18346                     }
18347                     
18348                     rm = true;
18349                     return;
18350                 }
18351             });
18352             
18353             if(rm){
18354                 return;
18355             }
18356             
18357             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18358                 this.tickItems.push(r.data);
18359             }
18360             
18361             if(typeof(e) == 'undefined' && view == false){
18362                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18363             }
18364                     
18365             return;
18366         }
18367         
18368         if(r){
18369             this.onSelect(r, index);
18370         }
18371         if(doFocus !== false && !this.blockFocus){
18372             this.inputEl().focus();
18373         }
18374     },
18375
18376     // private
18377     restrictHeight : function(){
18378         //this.innerList.dom.style.height = '';
18379         //var inner = this.innerList.dom;
18380         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18381         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18382         //this.list.beginUpdate();
18383         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18384         this.list.alignTo(this.inputEl(), this.listAlign);
18385         this.list.alignTo(this.inputEl(), this.listAlign);
18386         //this.list.endUpdate();
18387     },
18388
18389     // private
18390     onEmptyResults : function(){
18391         
18392         if(this.tickable && this.editable){
18393             this.hasFocus = false;
18394             this.restrictHeight();
18395             return;
18396         }
18397         
18398         this.collapse();
18399     },
18400
18401     /**
18402      * Returns true if the dropdown list is expanded, else false.
18403      */
18404     isExpanded : function(){
18405         return this.list.isVisible();
18406     },
18407
18408     /**
18409      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18410      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18411      * @param {String} value The data value of the item to select
18412      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18413      * selected item if it is not currently in view (defaults to true)
18414      * @return {Boolean} True if the value matched an item in the list, else false
18415      */
18416     selectByValue : function(v, scrollIntoView){
18417         if(v !== undefined && v !== null){
18418             var r = this.findRecord(this.valueField || this.displayField, v);
18419             if(r){
18420                 this.select(this.store.indexOf(r), scrollIntoView);
18421                 return true;
18422             }
18423         }
18424         return false;
18425     },
18426
18427     /**
18428      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18429      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18430      * @param {Number} index The zero-based index of the list item to select
18431      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18432      * selected item if it is not currently in view (defaults to true)
18433      */
18434     select : function(index, scrollIntoView){
18435         this.selectedIndex = index;
18436         this.view.select(index);
18437         if(scrollIntoView !== false){
18438             var el = this.view.getNode(index);
18439             /*
18440              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18441              */
18442             if(el){
18443                 this.list.scrollChildIntoView(el, false);
18444             }
18445         }
18446     },
18447
18448     // private
18449     selectNext : function(){
18450         var ct = this.store.getCount();
18451         if(ct > 0){
18452             if(this.selectedIndex == -1){
18453                 this.select(0);
18454             }else if(this.selectedIndex < ct-1){
18455                 this.select(this.selectedIndex+1);
18456             }
18457         }
18458     },
18459
18460     // private
18461     selectPrev : function(){
18462         var ct = this.store.getCount();
18463         if(ct > 0){
18464             if(this.selectedIndex == -1){
18465                 this.select(0);
18466             }else if(this.selectedIndex != 0){
18467                 this.select(this.selectedIndex-1);
18468             }
18469         }
18470     },
18471
18472     // private
18473     onKeyUp : function(e){
18474         if(this.editable !== false && !e.isSpecialKey()){
18475             this.lastKey = e.getKey();
18476             this.dqTask.delay(this.queryDelay);
18477         }
18478     },
18479
18480     // private
18481     validateBlur : function(){
18482         return !this.list || !this.list.isVisible();   
18483     },
18484
18485     // private
18486     initQuery : function(){
18487         
18488         var v = this.getRawValue();
18489         
18490         if(this.tickable && this.editable){
18491             v = this.tickableInputEl().getValue();
18492         }
18493         
18494         this.doQuery(v);
18495     },
18496
18497     // private
18498     doForce : function(){
18499         if(this.inputEl().dom.value.length > 0){
18500             this.inputEl().dom.value =
18501                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18502              
18503         }
18504     },
18505
18506     /**
18507      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18508      * query allowing the query action to be canceled if needed.
18509      * @param {String} query The SQL query to execute
18510      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18511      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18512      * saved in the current store (defaults to false)
18513      */
18514     doQuery : function(q, forceAll){
18515         
18516         if(q === undefined || q === null){
18517             q = '';
18518         }
18519         var qe = {
18520             query: q,
18521             forceAll: forceAll,
18522             combo: this,
18523             cancel:false
18524         };
18525         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18526             return false;
18527         }
18528         q = qe.query;
18529         
18530         forceAll = qe.forceAll;
18531         if(forceAll === true || (q.length >= this.minChars)){
18532             
18533             this.hasQuery = true;
18534             
18535             if(this.lastQuery != q || this.alwaysQuery){
18536                 this.lastQuery = q;
18537                 if(this.mode == 'local'){
18538                     this.selectedIndex = -1;
18539                     if(forceAll){
18540                         this.store.clearFilter();
18541                     }else{
18542                         
18543                         if(this.specialFilter){
18544                             this.fireEvent('specialfilter', this);
18545                             this.onLoad();
18546                             return;
18547                         }
18548                         
18549                         this.store.filter(this.displayField, q);
18550                     }
18551                     
18552                     this.store.fireEvent("datachanged", this.store);
18553                     
18554                     this.onLoad();
18555                     
18556                     
18557                 }else{
18558                     
18559                     this.store.baseParams[this.queryParam] = q;
18560                     
18561                     var options = {params : this.getParams(q)};
18562                     
18563                     if(this.loadNext){
18564                         options.add = true;
18565                         options.params.start = this.page * this.pageSize;
18566                     }
18567                     
18568                     this.store.load(options);
18569                     
18570                     /*
18571                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18572                      *  we should expand the list on onLoad
18573                      *  so command out it
18574                      */
18575 //                    this.expand();
18576                 }
18577             }else{
18578                 this.selectedIndex = -1;
18579                 this.onLoad();   
18580             }
18581         }
18582         
18583         this.loadNext = false;
18584     },
18585     
18586     // private
18587     getParams : function(q){
18588         var p = {};
18589         //p[this.queryParam] = q;
18590         
18591         if(this.pageSize){
18592             p.start = 0;
18593             p.limit = this.pageSize;
18594         }
18595         return p;
18596     },
18597
18598     /**
18599      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18600      */
18601     collapse : function(){
18602         if(!this.isExpanded()){
18603             return;
18604         }
18605         
18606         this.list.hide();
18607         
18608         this.hasFocus = false;
18609         
18610         if(this.tickable){
18611             this.okBtn.hide();
18612             this.cancelBtn.hide();
18613             this.trigger.show();
18614             
18615             if(this.editable){
18616                 this.tickableInputEl().dom.value = '';
18617                 this.tickableInputEl().blur();
18618             }
18619             
18620         }
18621         
18622         Roo.get(document).un('mousedown', this.collapseIf, this);
18623         Roo.get(document).un('mousewheel', this.collapseIf, this);
18624         if (!this.editable) {
18625             Roo.get(document).un('keydown', this.listKeyPress, this);
18626         }
18627         this.fireEvent('collapse', this);
18628         
18629         this.validate();
18630     },
18631
18632     // private
18633     collapseIf : function(e){
18634         var in_combo  = e.within(this.el);
18635         var in_list =  e.within(this.list);
18636         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18637         
18638         if (in_combo || in_list || is_list) {
18639             //e.stopPropagation();
18640             return;
18641         }
18642         
18643         if(this.tickable){
18644             this.onTickableFooterButtonClick(e, false, false);
18645         }
18646
18647         this.collapse();
18648         
18649     },
18650
18651     /**
18652      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18653      */
18654     expand : function(){
18655        
18656         if(this.isExpanded() || !this.hasFocus){
18657             return;
18658         }
18659         
18660         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18661         this.list.setWidth(lw);
18662         
18663         Roo.log('expand');
18664         
18665         this.list.show();
18666         
18667         this.restrictHeight();
18668         
18669         if(this.tickable){
18670             
18671             this.tickItems = Roo.apply([], this.item);
18672             
18673             this.okBtn.show();
18674             this.cancelBtn.show();
18675             this.trigger.hide();
18676             
18677             if(this.editable){
18678                 this.tickableInputEl().focus();
18679             }
18680             
18681         }
18682         
18683         Roo.get(document).on('mousedown', this.collapseIf, this);
18684         Roo.get(document).on('mousewheel', this.collapseIf, this);
18685         if (!this.editable) {
18686             Roo.get(document).on('keydown', this.listKeyPress, this);
18687         }
18688         
18689         this.fireEvent('expand', this);
18690     },
18691
18692     // private
18693     // Implements the default empty TriggerField.onTriggerClick function
18694     onTriggerClick : function(e)
18695     {
18696         Roo.log('trigger click');
18697         
18698         if(this.disabled || !this.triggerList){
18699             return;
18700         }
18701         
18702         this.page = 0;
18703         this.loadNext = false;
18704         
18705         if(this.isExpanded()){
18706             this.collapse();
18707             if (!this.blockFocus) {
18708                 this.inputEl().focus();
18709             }
18710             
18711         }else {
18712             this.hasFocus = true;
18713             if(this.triggerAction == 'all') {
18714                 this.doQuery(this.allQuery, true);
18715             } else {
18716                 this.doQuery(this.getRawValue());
18717             }
18718             if (!this.blockFocus) {
18719                 this.inputEl().focus();
18720             }
18721         }
18722     },
18723     
18724     onTickableTriggerClick : function(e)
18725     {
18726         if(this.disabled){
18727             return;
18728         }
18729         
18730         this.page = 0;
18731         this.loadNext = false;
18732         this.hasFocus = true;
18733         
18734         if(this.triggerAction == 'all') {
18735             this.doQuery(this.allQuery, true);
18736         } else {
18737             this.doQuery(this.getRawValue());
18738         }
18739     },
18740     
18741     onSearchFieldClick : function(e)
18742     {
18743         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18744             this.onTickableFooterButtonClick(e, false, false);
18745             return;
18746         }
18747         
18748         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18749             return;
18750         }
18751         
18752         this.page = 0;
18753         this.loadNext = false;
18754         this.hasFocus = true;
18755         
18756         if(this.triggerAction == 'all') {
18757             this.doQuery(this.allQuery, true);
18758         } else {
18759             this.doQuery(this.getRawValue());
18760         }
18761     },
18762     
18763     listKeyPress : function(e)
18764     {
18765         //Roo.log('listkeypress');
18766         // scroll to first matching element based on key pres..
18767         if (e.isSpecialKey()) {
18768             return false;
18769         }
18770         var k = String.fromCharCode(e.getKey()).toUpperCase();
18771         //Roo.log(k);
18772         var match  = false;
18773         var csel = this.view.getSelectedNodes();
18774         var cselitem = false;
18775         if (csel.length) {
18776             var ix = this.view.indexOf(csel[0]);
18777             cselitem  = this.store.getAt(ix);
18778             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18779                 cselitem = false;
18780             }
18781             
18782         }
18783         
18784         this.store.each(function(v) { 
18785             if (cselitem) {
18786                 // start at existing selection.
18787                 if (cselitem.id == v.id) {
18788                     cselitem = false;
18789                 }
18790                 return true;
18791             }
18792                 
18793             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18794                 match = this.store.indexOf(v);
18795                 return false;
18796             }
18797             return true;
18798         }, this);
18799         
18800         if (match === false) {
18801             return true; // no more action?
18802         }
18803         // scroll to?
18804         this.view.select(match);
18805         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18806         sn.scrollIntoView(sn.dom.parentNode, false);
18807     },
18808     
18809     onViewScroll : function(e, t){
18810         
18811         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){
18812             return;
18813         }
18814         
18815         this.hasQuery = true;
18816         
18817         this.loading = this.list.select('.loading', true).first();
18818         
18819         if(this.loading === null){
18820             this.list.createChild({
18821                 tag: 'div',
18822                 cls: 'loading roo-select2-more-results roo-select2-active',
18823                 html: 'Loading more results...'
18824             });
18825             
18826             this.loading = this.list.select('.loading', true).first();
18827             
18828             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18829             
18830             this.loading.hide();
18831         }
18832         
18833         this.loading.show();
18834         
18835         var _combo = this;
18836         
18837         this.page++;
18838         this.loadNext = true;
18839         
18840         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18841         
18842         return;
18843     },
18844     
18845     addItem : function(o)
18846     {   
18847         var dv = ''; // display value
18848         
18849         if (this.displayField) {
18850             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18851         } else {
18852             // this is an error condition!!!
18853             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18854         }
18855         
18856         if(!dv.length){
18857             return;
18858         }
18859         
18860         var choice = this.choices.createChild({
18861             tag: 'li',
18862             cls: 'roo-select2-search-choice',
18863             cn: [
18864                 {
18865                     tag: 'div',
18866                     html: dv
18867                 },
18868                 {
18869                     tag: 'a',
18870                     href: '#',
18871                     cls: 'roo-select2-search-choice-close fa fa-times',
18872                     tabindex: '-1'
18873                 }
18874             ]
18875             
18876         }, this.searchField);
18877         
18878         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18879         
18880         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18881         
18882         this.item.push(o);
18883         
18884         this.lastData = o;
18885         
18886         this.syncValue();
18887         
18888         this.inputEl().dom.value = '';
18889         
18890         this.validate();
18891     },
18892     
18893     onRemoveItem : function(e, _self, o)
18894     {
18895         e.preventDefault();
18896         
18897         this.lastItem = Roo.apply([], this.item);
18898         
18899         var index = this.item.indexOf(o.data) * 1;
18900         
18901         if( index < 0){
18902             Roo.log('not this item?!');
18903             return;
18904         }
18905         
18906         this.item.splice(index, 1);
18907         o.item.remove();
18908         
18909         this.syncValue();
18910         
18911         this.fireEvent('remove', this, e);
18912         
18913         this.validate();
18914         
18915     },
18916     
18917     syncValue : function()
18918     {
18919         if(!this.item.length){
18920             this.clearValue();
18921             return;
18922         }
18923             
18924         var value = [];
18925         var _this = this;
18926         Roo.each(this.item, function(i){
18927             if(_this.valueField){
18928                 value.push(i[_this.valueField]);
18929                 return;
18930             }
18931
18932             value.push(i);
18933         });
18934
18935         this.value = value.join(',');
18936
18937         if(this.hiddenField){
18938             this.hiddenField.dom.value = this.value;
18939         }
18940         
18941         this.store.fireEvent("datachanged", this.store);
18942         
18943         this.validate();
18944     },
18945     
18946     clearItem : function()
18947     {
18948         if(!this.multiple){
18949             return;
18950         }
18951         
18952         this.item = [];
18953         
18954         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18955            c.remove();
18956         });
18957         
18958         this.syncValue();
18959         
18960         this.validate();
18961         
18962         if(this.tickable && !Roo.isTouch){
18963             this.view.refresh();
18964         }
18965     },
18966     
18967     inputEl: function ()
18968     {
18969         if(Roo.isIOS && this.useNativeIOS){
18970             return this.el.select('select.roo-ios-select', true).first();
18971         }
18972         
18973         if(Roo.isTouch && this.mobileTouchView){
18974             return this.el.select('input.form-control',true).first();
18975         }
18976         
18977         if(this.tickable){
18978             return this.searchField;
18979         }
18980         
18981         return this.el.select('input.form-control',true).first();
18982     },
18983     
18984     onTickableFooterButtonClick : function(e, btn, el)
18985     {
18986         e.preventDefault();
18987         
18988         this.lastItem = Roo.apply([], this.item);
18989         
18990         if(btn && btn.name == 'cancel'){
18991             this.tickItems = Roo.apply([], this.item);
18992             this.collapse();
18993             return;
18994         }
18995         
18996         this.clearItem();
18997         
18998         var _this = this;
18999         
19000         Roo.each(this.tickItems, function(o){
19001             _this.addItem(o);
19002         });
19003         
19004         this.collapse();
19005         
19006     },
19007     
19008     validate : function()
19009     {
19010         if(this.getVisibilityEl().hasClass('hidden')){
19011             return true;
19012         }
19013         
19014         var v = this.getRawValue();
19015         
19016         if(this.multiple){
19017             v = this.getValue();
19018         }
19019         
19020         if(this.disabled || this.allowBlank || v.length){
19021             this.markValid();
19022             return true;
19023         }
19024         
19025         this.markInvalid();
19026         return false;
19027     },
19028     
19029     tickableInputEl : function()
19030     {
19031         if(!this.tickable || !this.editable){
19032             return this.inputEl();
19033         }
19034         
19035         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19036     },
19037     
19038     
19039     getAutoCreateTouchView : function()
19040     {
19041         var id = Roo.id();
19042         
19043         var cfg = {
19044             cls: 'form-group' //input-group
19045         };
19046         
19047         var input =  {
19048             tag: 'input',
19049             id : id,
19050             type : this.inputType,
19051             cls : 'form-control x-combo-noedit',
19052             autocomplete: 'new-password',
19053             placeholder : this.placeholder || '',
19054             readonly : true
19055         };
19056         
19057         if (this.name) {
19058             input.name = this.name;
19059         }
19060         
19061         if (this.size) {
19062             input.cls += ' input-' + this.size;
19063         }
19064         
19065         if (this.disabled) {
19066             input.disabled = true;
19067         }
19068         
19069         var inputblock = {
19070             cls : 'roo-combobox-wrap',
19071             cn : [
19072                 input
19073             ]
19074         };
19075         
19076         if(this.before){
19077             inputblock.cls += ' input-group';
19078             
19079             inputblock.cn.unshift({
19080                 tag :'span',
19081                 cls : 'input-group-addon input-group-prepend input-group-text',
19082                 html : this.before
19083             });
19084         }
19085         
19086         if(this.removable && !this.multiple){
19087             inputblock.cls += ' roo-removable';
19088             
19089             inputblock.cn.push({
19090                 tag: 'button',
19091                 html : 'x',
19092                 cls : 'roo-combo-removable-btn close'
19093             });
19094         }
19095
19096         if(this.hasFeedback && !this.allowBlank){
19097             
19098             inputblock.cls += ' has-feedback';
19099             
19100             inputblock.cn.push({
19101                 tag: 'span',
19102                 cls: 'glyphicon form-control-feedback'
19103             });
19104             
19105         }
19106         
19107         if (this.after) {
19108             
19109             inputblock.cls += (this.before) ? '' : ' input-group';
19110             
19111             inputblock.cn.push({
19112                 tag :'span',
19113                 cls : 'input-group-addon input-group-append input-group-text',
19114                 html : this.after
19115             });
19116         }
19117
19118         
19119         var ibwrap = inputblock;
19120         
19121         if(this.multiple){
19122             ibwrap = {
19123                 tag: 'ul',
19124                 cls: 'roo-select2-choices',
19125                 cn:[
19126                     {
19127                         tag: 'li',
19128                         cls: 'roo-select2-search-field',
19129                         cn: [
19130
19131                             inputblock
19132                         ]
19133                     }
19134                 ]
19135             };
19136         
19137             
19138         }
19139         
19140         var combobox = {
19141             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19142             cn: [
19143                 {
19144                     tag: 'input',
19145                     type : 'hidden',
19146                     cls: 'form-hidden-field'
19147                 },
19148                 ibwrap
19149             ]
19150         };
19151         
19152         if(!this.multiple && this.showToggleBtn){
19153             
19154             var caret = {
19155                 cls: 'caret'
19156             };
19157             
19158             if (this.caret != false) {
19159                 caret = {
19160                      tag: 'i',
19161                      cls: 'fa fa-' + this.caret
19162                 };
19163                 
19164             }
19165             
19166             combobox.cn.push({
19167                 tag :'span',
19168                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19169                 cn : [
19170                     Roo.bootstrap.version == 3 ? caret : '',
19171                     {
19172                         tag: 'span',
19173                         cls: 'combobox-clear',
19174                         cn  : [
19175                             {
19176                                 tag : 'i',
19177                                 cls: 'icon-remove'
19178                             }
19179                         ]
19180                     }
19181                 ]
19182
19183             })
19184         }
19185         
19186         if(this.multiple){
19187             combobox.cls += ' roo-select2-container-multi';
19188         }
19189         
19190         var required =  this.allowBlank ?  {
19191                     tag : 'i',
19192                     style: 'display: none'
19193                 } : {
19194                    tag : 'i',
19195                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19196                    tooltip : 'This field is required'
19197                 };
19198         
19199         var align = this.labelAlign || this.parentLabelAlign();
19200         
19201         if (align ==='left' && this.fieldLabel.length) {
19202
19203             cfg.cn = [
19204                 required,
19205                 {
19206                     tag: 'label',
19207                     cls : 'control-label col-form-label',
19208                     html : this.fieldLabel
19209
19210                 },
19211                 {
19212                     cls : 'roo-combobox-wrap ', 
19213                     cn: [
19214                         combobox
19215                     ]
19216                 }
19217             ];
19218             
19219             var labelCfg = cfg.cn[1];
19220             var contentCfg = cfg.cn[2];
19221             
19222
19223             if(this.indicatorpos == 'right'){
19224                 cfg.cn = [
19225                     {
19226                         tag: 'label',
19227                         'for' :  id,
19228                         cls : 'control-label col-form-label',
19229                         cn : [
19230                             {
19231                                 tag : 'span',
19232                                 html : this.fieldLabel
19233                             },
19234                             required
19235                         ]
19236                     },
19237                     {
19238                         cls : "roo-combobox-wrap ",
19239                         cn: [
19240                             combobox
19241                         ]
19242                     }
19243
19244                 ];
19245                 
19246                 labelCfg = cfg.cn[0];
19247                 contentCfg = cfg.cn[1];
19248             }
19249             
19250            
19251             
19252             if(this.labelWidth > 12){
19253                 labelCfg.style = "width: " + this.labelWidth + 'px';
19254             }
19255            
19256             if(this.labelWidth < 13 && this.labelmd == 0){
19257                 this.labelmd = this.labelWidth;
19258             }
19259             
19260             if(this.labellg > 0){
19261                 labelCfg.cls += ' col-lg-' + this.labellg;
19262                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19263             }
19264             
19265             if(this.labelmd > 0){
19266                 labelCfg.cls += ' col-md-' + this.labelmd;
19267                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19268             }
19269             
19270             if(this.labelsm > 0){
19271                 labelCfg.cls += ' col-sm-' + this.labelsm;
19272                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19273             }
19274             
19275             if(this.labelxs > 0){
19276                 labelCfg.cls += ' col-xs-' + this.labelxs;
19277                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19278             }
19279                 
19280                 
19281         } else if ( this.fieldLabel.length) {
19282             cfg.cn = [
19283                required,
19284                 {
19285                     tag: 'label',
19286                     cls : 'control-label',
19287                     html : this.fieldLabel
19288
19289                 },
19290                 {
19291                     cls : '', 
19292                     cn: [
19293                         combobox
19294                     ]
19295                 }
19296             ];
19297             
19298             if(this.indicatorpos == 'right'){
19299                 cfg.cn = [
19300                     {
19301                         tag: 'label',
19302                         cls : 'control-label',
19303                         html : this.fieldLabel,
19304                         cn : [
19305                             required
19306                         ]
19307                     },
19308                     {
19309                         cls : '', 
19310                         cn: [
19311                             combobox
19312                         ]
19313                     }
19314                 ];
19315             }
19316         } else {
19317             cfg.cn = combobox;    
19318         }
19319         
19320         
19321         var settings = this;
19322         
19323         ['xs','sm','md','lg'].map(function(size){
19324             if (settings[size]) {
19325                 cfg.cls += ' col-' + size + '-' + settings[size];
19326             }
19327         });
19328         
19329         return cfg;
19330     },
19331     
19332     initTouchView : function()
19333     {
19334         this.renderTouchView();
19335         
19336         this.touchViewEl.on('scroll', function(){
19337             this.el.dom.scrollTop = 0;
19338         }, this);
19339         
19340         this.originalValue = this.getValue();
19341         
19342         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19343         
19344         this.inputEl().on("click", this.showTouchView, this);
19345         if (this.triggerEl) {
19346             this.triggerEl.on("click", this.showTouchView, this);
19347         }
19348         
19349         
19350         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19351         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19352         
19353         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19354         
19355         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19356         this.store.on('load', this.onTouchViewLoad, this);
19357         this.store.on('loadexception', this.onTouchViewLoadException, this);
19358         
19359         if(this.hiddenName){
19360             
19361             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19362             
19363             this.hiddenField.dom.value =
19364                 this.hiddenValue !== undefined ? this.hiddenValue :
19365                 this.value !== undefined ? this.value : '';
19366         
19367             this.el.dom.removeAttribute('name');
19368             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19369         }
19370         
19371         if(this.multiple){
19372             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19373             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19374         }
19375         
19376         if(this.removable && !this.multiple){
19377             var close = this.closeTriggerEl();
19378             if(close){
19379                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19380                 close.on('click', this.removeBtnClick, this, close);
19381             }
19382         }
19383         /*
19384          * fix the bug in Safari iOS8
19385          */
19386         this.inputEl().on("focus", function(e){
19387             document.activeElement.blur();
19388         }, this);
19389         
19390         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19391         
19392         return;
19393         
19394         
19395     },
19396     
19397     renderTouchView : function()
19398     {
19399         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19400         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19401         
19402         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19403         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19404         
19405         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19406         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19407         this.touchViewBodyEl.setStyle('overflow', 'auto');
19408         
19409         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19410         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19411         
19412         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19413         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19414         
19415     },
19416     
19417     showTouchView : function()
19418     {
19419         if(this.disabled){
19420             return;
19421         }
19422         
19423         this.touchViewHeaderEl.hide();
19424
19425         if(this.modalTitle.length){
19426             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19427             this.touchViewHeaderEl.show();
19428         }
19429
19430         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19431         this.touchViewEl.show();
19432
19433         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19434         
19435         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19436         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19437
19438         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19439
19440         if(this.modalTitle.length){
19441             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19442         }
19443         
19444         this.touchViewBodyEl.setHeight(bodyHeight);
19445
19446         if(this.animate){
19447             var _this = this;
19448             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19449         }else{
19450             this.touchViewEl.addClass(['in','show']);
19451         }
19452         
19453         if(this._touchViewMask){
19454             Roo.get(document.body).addClass("x-body-masked");
19455             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19456             this._touchViewMask.setStyle('z-index', 10000);
19457             this._touchViewMask.addClass('show');
19458         }
19459         
19460         this.doTouchViewQuery();
19461         
19462     },
19463     
19464     hideTouchView : function()
19465     {
19466         this.touchViewEl.removeClass(['in','show']);
19467
19468         if(this.animate){
19469             var _this = this;
19470             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19471         }else{
19472             this.touchViewEl.setStyle('display', 'none');
19473         }
19474         
19475         if(this._touchViewMask){
19476             this._touchViewMask.removeClass('show');
19477             Roo.get(document.body).removeClass("x-body-masked");
19478         }
19479     },
19480     
19481     setTouchViewValue : function()
19482     {
19483         if(this.multiple){
19484             this.clearItem();
19485         
19486             var _this = this;
19487
19488             Roo.each(this.tickItems, function(o){
19489                 this.addItem(o);
19490             }, this);
19491         }
19492         
19493         this.hideTouchView();
19494     },
19495     
19496     doTouchViewQuery : function()
19497     {
19498         var qe = {
19499             query: '',
19500             forceAll: true,
19501             combo: this,
19502             cancel:false
19503         };
19504         
19505         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19506             return false;
19507         }
19508         
19509         if(!this.alwaysQuery || this.mode == 'local'){
19510             this.onTouchViewLoad();
19511             return;
19512         }
19513         
19514         this.store.load();
19515     },
19516     
19517     onTouchViewBeforeLoad : function(combo,opts)
19518     {
19519         return;
19520     },
19521
19522     // private
19523     onTouchViewLoad : function()
19524     {
19525         if(this.store.getCount() < 1){
19526             this.onTouchViewEmptyResults();
19527             return;
19528         }
19529         
19530         this.clearTouchView();
19531         
19532         var rawValue = this.getRawValue();
19533         
19534         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19535         
19536         this.tickItems = [];
19537         
19538         this.store.data.each(function(d, rowIndex){
19539             var row = this.touchViewListGroup.createChild(template);
19540             
19541             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19542                 row.addClass(d.data.cls);
19543             }
19544             
19545             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19546                 var cfg = {
19547                     data : d.data,
19548                     html : d.data[this.displayField]
19549                 };
19550                 
19551                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19552                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19553                 }
19554             }
19555             row.removeClass('selected');
19556             if(!this.multiple && this.valueField &&
19557                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19558             {
19559                 // radio buttons..
19560                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19561                 row.addClass('selected');
19562             }
19563             
19564             if(this.multiple && this.valueField &&
19565                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19566             {
19567                 
19568                 // checkboxes...
19569                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19570                 this.tickItems.push(d.data);
19571             }
19572             
19573             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19574             
19575         }, this);
19576         
19577         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19578         
19579         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19580
19581         if(this.modalTitle.length){
19582             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19583         }
19584
19585         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19586         
19587         if(this.mobile_restrict_height && listHeight < bodyHeight){
19588             this.touchViewBodyEl.setHeight(listHeight);
19589         }
19590         
19591         var _this = this;
19592         
19593         if(firstChecked && listHeight > bodyHeight){
19594             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19595         }
19596         
19597     },
19598     
19599     onTouchViewLoadException : function()
19600     {
19601         this.hideTouchView();
19602     },
19603     
19604     onTouchViewEmptyResults : function()
19605     {
19606         this.clearTouchView();
19607         
19608         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19609         
19610         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19611         
19612     },
19613     
19614     clearTouchView : function()
19615     {
19616         this.touchViewListGroup.dom.innerHTML = '';
19617     },
19618     
19619     onTouchViewClick : function(e, el, o)
19620     {
19621         e.preventDefault();
19622         
19623         var row = o.row;
19624         var rowIndex = o.rowIndex;
19625         
19626         var r = this.store.getAt(rowIndex);
19627         
19628         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19629             
19630             if(!this.multiple){
19631                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19632                     c.dom.removeAttribute('checked');
19633                 }, this);
19634
19635                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19636
19637                 this.setFromData(r.data);
19638
19639                 var close = this.closeTriggerEl();
19640
19641                 if(close){
19642                     close.show();
19643                 }
19644
19645                 this.hideTouchView();
19646
19647                 this.fireEvent('select', this, r, rowIndex);
19648
19649                 return;
19650             }
19651
19652             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19653                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19654                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19655                 return;
19656             }
19657
19658             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19659             this.addItem(r.data);
19660             this.tickItems.push(r.data);
19661         }
19662     },
19663     
19664     getAutoCreateNativeIOS : function()
19665     {
19666         var cfg = {
19667             cls: 'form-group' //input-group,
19668         };
19669         
19670         var combobox =  {
19671             tag: 'select',
19672             cls : 'roo-ios-select'
19673         };
19674         
19675         if (this.name) {
19676             combobox.name = this.name;
19677         }
19678         
19679         if (this.disabled) {
19680             combobox.disabled = true;
19681         }
19682         
19683         var settings = this;
19684         
19685         ['xs','sm','md','lg'].map(function(size){
19686             if (settings[size]) {
19687                 cfg.cls += ' col-' + size + '-' + settings[size];
19688             }
19689         });
19690         
19691         cfg.cn = combobox;
19692         
19693         return cfg;
19694         
19695     },
19696     
19697     initIOSView : function()
19698     {
19699         this.store.on('load', this.onIOSViewLoad, this);
19700         
19701         return;
19702     },
19703     
19704     onIOSViewLoad : function()
19705     {
19706         if(this.store.getCount() < 1){
19707             return;
19708         }
19709         
19710         this.clearIOSView();
19711         
19712         if(this.allowBlank) {
19713             
19714             var default_text = '-- SELECT --';
19715             
19716             if(this.placeholder.length){
19717                 default_text = this.placeholder;
19718             }
19719             
19720             if(this.emptyTitle.length){
19721                 default_text += ' - ' + this.emptyTitle + ' -';
19722             }
19723             
19724             var opt = this.inputEl().createChild({
19725                 tag: 'option',
19726                 value : 0,
19727                 html : default_text
19728             });
19729             
19730             var o = {};
19731             o[this.valueField] = 0;
19732             o[this.displayField] = default_text;
19733             
19734             this.ios_options.push({
19735                 data : o,
19736                 el : opt
19737             });
19738             
19739         }
19740         
19741         this.store.data.each(function(d, rowIndex){
19742             
19743             var html = '';
19744             
19745             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19746                 html = d.data[this.displayField];
19747             }
19748             
19749             var value = '';
19750             
19751             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19752                 value = d.data[this.valueField];
19753             }
19754             
19755             var option = {
19756                 tag: 'option',
19757                 value : value,
19758                 html : html
19759             };
19760             
19761             if(this.value == d.data[this.valueField]){
19762                 option['selected'] = true;
19763             }
19764             
19765             var opt = this.inputEl().createChild(option);
19766             
19767             this.ios_options.push({
19768                 data : d.data,
19769                 el : opt
19770             });
19771             
19772         }, this);
19773         
19774         this.inputEl().on('change', function(){
19775            this.fireEvent('select', this);
19776         }, this);
19777         
19778     },
19779     
19780     clearIOSView: function()
19781     {
19782         this.inputEl().dom.innerHTML = '';
19783         
19784         this.ios_options = [];
19785     },
19786     
19787     setIOSValue: function(v)
19788     {
19789         this.value = v;
19790         
19791         if(!this.ios_options){
19792             return;
19793         }
19794         
19795         Roo.each(this.ios_options, function(opts){
19796            
19797            opts.el.dom.removeAttribute('selected');
19798            
19799            if(opts.data[this.valueField] != v){
19800                return;
19801            }
19802            
19803            opts.el.dom.setAttribute('selected', true);
19804            
19805         }, this);
19806     }
19807
19808     /** 
19809     * @cfg {Boolean} grow 
19810     * @hide 
19811     */
19812     /** 
19813     * @cfg {Number} growMin 
19814     * @hide 
19815     */
19816     /** 
19817     * @cfg {Number} growMax 
19818     * @hide 
19819     */
19820     /**
19821      * @hide
19822      * @method autoSize
19823      */
19824 });
19825
19826 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19827     
19828     header : {
19829         tag: 'div',
19830         cls: 'modal-header',
19831         cn: [
19832             {
19833                 tag: 'h4',
19834                 cls: 'modal-title'
19835             }
19836         ]
19837     },
19838     
19839     body : {
19840         tag: 'div',
19841         cls: 'modal-body',
19842         cn: [
19843             {
19844                 tag: 'ul',
19845                 cls: 'list-group'
19846             }
19847         ]
19848     },
19849     
19850     listItemRadio : {
19851         tag: 'li',
19852         cls: 'list-group-item',
19853         cn: [
19854             {
19855                 tag: 'span',
19856                 cls: 'roo-combobox-list-group-item-value'
19857             },
19858             {
19859                 tag: 'div',
19860                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19861                 cn: [
19862                     {
19863                         tag: 'input',
19864                         type: 'radio'
19865                     },
19866                     {
19867                         tag: 'label'
19868                     }
19869                 ]
19870             }
19871         ]
19872     },
19873     
19874     listItemCheckbox : {
19875         tag: 'li',
19876         cls: 'list-group-item',
19877         cn: [
19878             {
19879                 tag: 'span',
19880                 cls: 'roo-combobox-list-group-item-value'
19881             },
19882             {
19883                 tag: 'div',
19884                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19885                 cn: [
19886                     {
19887                         tag: 'input',
19888                         type: 'checkbox'
19889                     },
19890                     {
19891                         tag: 'label'
19892                     }
19893                 ]
19894             }
19895         ]
19896     },
19897     
19898     emptyResult : {
19899         tag: 'div',
19900         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19901     },
19902     
19903     footer : {
19904         tag: 'div',
19905         cls: 'modal-footer',
19906         cn: [
19907             {
19908                 tag: 'div',
19909                 cls: 'row',
19910                 cn: [
19911                     {
19912                         tag: 'div',
19913                         cls: 'col-xs-6 text-left',
19914                         cn: {
19915                             tag: 'button',
19916                             cls: 'btn btn-danger roo-touch-view-cancel',
19917                             html: 'Cancel'
19918                         }
19919                     },
19920                     {
19921                         tag: 'div',
19922                         cls: 'col-xs-6 text-right',
19923                         cn: {
19924                             tag: 'button',
19925                             cls: 'btn btn-success roo-touch-view-ok',
19926                             html: 'OK'
19927                         }
19928                     }
19929                 ]
19930             }
19931         ]
19932         
19933     }
19934 });
19935
19936 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19937     
19938     touchViewTemplate : {
19939         tag: 'div',
19940         cls: 'modal fade roo-combobox-touch-view',
19941         cn: [
19942             {
19943                 tag: 'div',
19944                 cls: 'modal-dialog',
19945                 style : 'position:fixed', // we have to fix position....
19946                 cn: [
19947                     {
19948                         tag: 'div',
19949                         cls: 'modal-content',
19950                         cn: [
19951                             Roo.bootstrap.form.ComboBox.header,
19952                             Roo.bootstrap.form.ComboBox.body,
19953                             Roo.bootstrap.form.ComboBox.footer
19954                         ]
19955                     }
19956                 ]
19957             }
19958         ]
19959     }
19960 });/*
19961  * Based on:
19962  * Ext JS Library 1.1.1
19963  * Copyright(c) 2006-2007, Ext JS, LLC.
19964  *
19965  * Originally Released Under LGPL - original licence link has changed is not relivant.
19966  *
19967  * Fork - LGPL
19968  * <script type="text/javascript">
19969  */
19970
19971 /**
19972  * @class Roo.View
19973  * @extends Roo.util.Observable
19974  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19975  * This class also supports single and multi selection modes. <br>
19976  * Create a data model bound view:
19977  <pre><code>
19978  var store = new Roo.data.Store(...);
19979
19980  var view = new Roo.View({
19981     el : "my-element",
19982     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19983  
19984     singleSelect: true,
19985     selectedClass: "ydataview-selected",
19986     store: store
19987  });
19988
19989  // listen for node click?
19990  view.on("click", function(vw, index, node, e){
19991  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19992  });
19993
19994  // load XML data
19995  dataModel.load("foobar.xml");
19996  </code></pre>
19997  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19998  * <br><br>
19999  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
20000  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
20001  * 
20002  * Note: old style constructor is still suported (container, template, config)
20003  * 
20004  * @constructor
20005  * Create a new View
20006  * @param {Object} config The config object
20007  * 
20008  */
20009 Roo.View = function(config, depreciated_tpl, depreciated_config){
20010     
20011     this.parent = false;
20012     
20013     if (typeof(depreciated_tpl) == 'undefined') {
20014         // new way.. - universal constructor.
20015         Roo.apply(this, config);
20016         this.el  = Roo.get(this.el);
20017     } else {
20018         // old format..
20019         this.el  = Roo.get(config);
20020         this.tpl = depreciated_tpl;
20021         Roo.apply(this, depreciated_config);
20022     }
20023     this.wrapEl  = this.el.wrap().wrap();
20024     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20025     
20026     
20027     if(typeof(this.tpl) == "string"){
20028         this.tpl = new Roo.Template(this.tpl);
20029     } else {
20030         // support xtype ctors..
20031         this.tpl = new Roo.factory(this.tpl, Roo);
20032     }
20033     
20034     
20035     this.tpl.compile();
20036     
20037     /** @private */
20038     this.addEvents({
20039         /**
20040          * @event beforeclick
20041          * Fires before a click is processed. Returns false to cancel the default action.
20042          * @param {Roo.View} this
20043          * @param {Number} index The index of the target node
20044          * @param {HTMLElement} node The target node
20045          * @param {Roo.EventObject} e The raw event object
20046          */
20047             "beforeclick" : true,
20048         /**
20049          * @event click
20050          * Fires when a template node is clicked.
20051          * @param {Roo.View} this
20052          * @param {Number} index The index of the target node
20053          * @param {HTMLElement} node The target node
20054          * @param {Roo.EventObject} e The raw event object
20055          */
20056             "click" : true,
20057         /**
20058          * @event dblclick
20059          * Fires when a template node is double clicked.
20060          * @param {Roo.View} this
20061          * @param {Number} index The index of the target node
20062          * @param {HTMLElement} node The target node
20063          * @param {Roo.EventObject} e The raw event object
20064          */
20065             "dblclick" : true,
20066         /**
20067          * @event contextmenu
20068          * Fires when a template node is right clicked.
20069          * @param {Roo.View} this
20070          * @param {Number} index The index of the target node
20071          * @param {HTMLElement} node The target node
20072          * @param {Roo.EventObject} e The raw event object
20073          */
20074             "contextmenu" : true,
20075         /**
20076          * @event selectionchange
20077          * Fires when the selected nodes change.
20078          * @param {Roo.View} this
20079          * @param {Array} selections Array of the selected nodes
20080          */
20081             "selectionchange" : true,
20082     
20083         /**
20084          * @event beforeselect
20085          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20086          * @param {Roo.View} this
20087          * @param {HTMLElement} node The node to be selected
20088          * @param {Array} selections Array of currently selected nodes
20089          */
20090             "beforeselect" : true,
20091         /**
20092          * @event preparedata
20093          * Fires on every row to render, to allow you to change the data.
20094          * @param {Roo.View} this
20095          * @param {Object} data to be rendered (change this)
20096          */
20097           "preparedata" : true
20098           
20099           
20100         });
20101
20102
20103
20104     this.el.on({
20105         "click": this.onClick,
20106         "dblclick": this.onDblClick,
20107         "contextmenu": this.onContextMenu,
20108         scope:this
20109     });
20110
20111     this.selections = [];
20112     this.nodes = [];
20113     this.cmp = new Roo.CompositeElementLite([]);
20114     if(this.store){
20115         this.store = Roo.factory(this.store, Roo.data);
20116         this.setStore(this.store, true);
20117     }
20118     
20119     if ( this.footer && this.footer.xtype) {
20120            
20121          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20122         
20123         this.footer.dataSource = this.store;
20124         this.footer.container = fctr;
20125         this.footer = Roo.factory(this.footer, Roo);
20126         fctr.insertFirst(this.el);
20127         
20128         // this is a bit insane - as the paging toolbar seems to detach the el..
20129 //        dom.parentNode.parentNode.parentNode
20130          // they get detached?
20131     }
20132     
20133     
20134     Roo.View.superclass.constructor.call(this);
20135     
20136     
20137 };
20138
20139 Roo.extend(Roo.View, Roo.util.Observable, {
20140     
20141      /**
20142      * @cfg {Roo.data.Store} store Data store to load data from.
20143      */
20144     store : false,
20145     
20146     /**
20147      * @cfg {String|Roo.Element} el The container element.
20148      */
20149     el : '',
20150     
20151     /**
20152      * @cfg {String|Roo.Template} tpl The template used by this View 
20153      */
20154     tpl : false,
20155     /**
20156      * @cfg {String} dataName the named area of the template to use as the data area
20157      *                          Works with domtemplates roo-name="name"
20158      */
20159     dataName: false,
20160     /**
20161      * @cfg {String} selectedClass The css class to add to selected nodes
20162      */
20163     selectedClass : "x-view-selected",
20164      /**
20165      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20166      */
20167     emptyText : "",
20168     
20169     /**
20170      * @cfg {String} text to display on mask (default Loading)
20171      */
20172     mask : false,
20173     /**
20174      * @cfg {Boolean} multiSelect Allow multiple selection
20175      */
20176     multiSelect : false,
20177     /**
20178      * @cfg {Boolean} singleSelect Allow single selection
20179      */
20180     singleSelect:  false,
20181     
20182     /**
20183      * @cfg {Boolean} toggleSelect - selecting 
20184      */
20185     toggleSelect : false,
20186     
20187     /**
20188      * @cfg {Boolean} tickable - selecting 
20189      */
20190     tickable : false,
20191     
20192     /**
20193      * Returns the element this view is bound to.
20194      * @return {Roo.Element}
20195      */
20196     getEl : function(){
20197         return this.wrapEl;
20198     },
20199     
20200     
20201
20202     /**
20203      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20204      */
20205     refresh : function(){
20206         //Roo.log('refresh');
20207         var t = this.tpl;
20208         
20209         // if we are using something like 'domtemplate', then
20210         // the what gets used is:
20211         // t.applySubtemplate(NAME, data, wrapping data..)
20212         // the outer template then get' applied with
20213         //     the store 'extra data'
20214         // and the body get's added to the
20215         //      roo-name="data" node?
20216         //      <span class='roo-tpl-{name}'></span> ?????
20217         
20218         
20219         
20220         this.clearSelections();
20221         this.el.update("");
20222         var html = [];
20223         var records = this.store.getRange();
20224         if(records.length < 1) {
20225             
20226             // is this valid??  = should it render a template??
20227             
20228             this.el.update(this.emptyText);
20229             return;
20230         }
20231         var el = this.el;
20232         if (this.dataName) {
20233             this.el.update(t.apply(this.store.meta)); //????
20234             el = this.el.child('.roo-tpl-' + this.dataName);
20235         }
20236         
20237         for(var i = 0, len = records.length; i < len; i++){
20238             var data = this.prepareData(records[i].data, i, records[i]);
20239             this.fireEvent("preparedata", this, data, i, records[i]);
20240             
20241             var d = Roo.apply({}, data);
20242             
20243             if(this.tickable){
20244                 Roo.apply(d, {'roo-id' : Roo.id()});
20245                 
20246                 var _this = this;
20247             
20248                 Roo.each(this.parent.item, function(item){
20249                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20250                         return;
20251                     }
20252                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20253                 });
20254             }
20255             
20256             html[html.length] = Roo.util.Format.trim(
20257                 this.dataName ?
20258                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20259                     t.apply(d)
20260             );
20261         }
20262         
20263         
20264         
20265         el.update(html.join(""));
20266         this.nodes = el.dom.childNodes;
20267         this.updateIndexes(0);
20268     },
20269     
20270
20271     /**
20272      * Function to override to reformat the data that is sent to
20273      * the template for each node.
20274      * DEPRICATED - use the preparedata event handler.
20275      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20276      * a JSON object for an UpdateManager bound view).
20277      */
20278     prepareData : function(data, index, record)
20279     {
20280         this.fireEvent("preparedata", this, data, index, record);
20281         return data;
20282     },
20283
20284     onUpdate : function(ds, record){
20285         // Roo.log('on update');   
20286         this.clearSelections();
20287         var index = this.store.indexOf(record);
20288         var n = this.nodes[index];
20289         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20290         n.parentNode.removeChild(n);
20291         this.updateIndexes(index, index);
20292     },
20293
20294     
20295     
20296 // --------- FIXME     
20297     onAdd : function(ds, records, index)
20298     {
20299         //Roo.log(['on Add', ds, records, index] );        
20300         this.clearSelections();
20301         if(this.nodes.length == 0){
20302             this.refresh();
20303             return;
20304         }
20305         var n = this.nodes[index];
20306         for(var i = 0, len = records.length; i < len; i++){
20307             var d = this.prepareData(records[i].data, i, records[i]);
20308             if(n){
20309                 this.tpl.insertBefore(n, d);
20310             }else{
20311                 
20312                 this.tpl.append(this.el, d);
20313             }
20314         }
20315         this.updateIndexes(index);
20316     },
20317
20318     onRemove : function(ds, record, index){
20319        // Roo.log('onRemove');
20320         this.clearSelections();
20321         var el = this.dataName  ?
20322             this.el.child('.roo-tpl-' + this.dataName) :
20323             this.el; 
20324         
20325         el.dom.removeChild(this.nodes[index]);
20326         this.updateIndexes(index);
20327     },
20328
20329     /**
20330      * Refresh an individual node.
20331      * @param {Number} index
20332      */
20333     refreshNode : function(index){
20334         this.onUpdate(this.store, this.store.getAt(index));
20335     },
20336
20337     updateIndexes : function(startIndex, endIndex){
20338         var ns = this.nodes;
20339         startIndex = startIndex || 0;
20340         endIndex = endIndex || ns.length - 1;
20341         for(var i = startIndex; i <= endIndex; i++){
20342             ns[i].nodeIndex = i;
20343         }
20344     },
20345
20346     /**
20347      * Changes the data store this view uses and refresh the view.
20348      * @param {Store} store
20349      */
20350     setStore : function(store, initial){
20351         if(!initial && this.store){
20352             this.store.un("datachanged", this.refresh);
20353             this.store.un("add", this.onAdd);
20354             this.store.un("remove", this.onRemove);
20355             this.store.un("update", this.onUpdate);
20356             this.store.un("clear", this.refresh);
20357             this.store.un("beforeload", this.onBeforeLoad);
20358             this.store.un("load", this.onLoad);
20359             this.store.un("loadexception", this.onLoad);
20360         }
20361         if(store){
20362           
20363             store.on("datachanged", this.refresh, this);
20364             store.on("add", this.onAdd, this);
20365             store.on("remove", this.onRemove, this);
20366             store.on("update", this.onUpdate, this);
20367             store.on("clear", this.refresh, this);
20368             store.on("beforeload", this.onBeforeLoad, this);
20369             store.on("load", this.onLoad, this);
20370             store.on("loadexception", this.onLoad, this);
20371         }
20372         
20373         if(store){
20374             this.refresh();
20375         }
20376     },
20377     /**
20378      * onbeforeLoad - masks the loading area.
20379      *
20380      */
20381     onBeforeLoad : function(store,opts)
20382     {
20383          //Roo.log('onBeforeLoad');   
20384         if (!opts.add) {
20385             this.el.update("");
20386         }
20387         this.el.mask(this.mask ? this.mask : "Loading" ); 
20388     },
20389     onLoad : function ()
20390     {
20391         this.el.unmask();
20392     },
20393     
20394
20395     /**
20396      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20397      * @param {HTMLElement} node
20398      * @return {HTMLElement} The template node
20399      */
20400     findItemFromChild : function(node){
20401         var el = this.dataName  ?
20402             this.el.child('.roo-tpl-' + this.dataName,true) :
20403             this.el.dom; 
20404         
20405         if(!node || node.parentNode == el){
20406                     return node;
20407             }
20408             var p = node.parentNode;
20409             while(p && p != el){
20410             if(p.parentNode == el){
20411                 return p;
20412             }
20413             p = p.parentNode;
20414         }
20415             return null;
20416     },
20417
20418     /** @ignore */
20419     onClick : function(e){
20420         var item = this.findItemFromChild(e.getTarget());
20421         if(item){
20422             var index = this.indexOf(item);
20423             if(this.onItemClick(item, index, e) !== false){
20424                 this.fireEvent("click", this, index, item, e);
20425             }
20426         }else{
20427             this.clearSelections();
20428         }
20429     },
20430
20431     /** @ignore */
20432     onContextMenu : function(e){
20433         var item = this.findItemFromChild(e.getTarget());
20434         if(item){
20435             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20436         }
20437     },
20438
20439     /** @ignore */
20440     onDblClick : function(e){
20441         var item = this.findItemFromChild(e.getTarget());
20442         if(item){
20443             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20444         }
20445     },
20446
20447     onItemClick : function(item, index, e)
20448     {
20449         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20450             return false;
20451         }
20452         if (this.toggleSelect) {
20453             var m = this.isSelected(item) ? 'unselect' : 'select';
20454             //Roo.log(m);
20455             var _t = this;
20456             _t[m](item, true, false);
20457             return true;
20458         }
20459         if(this.multiSelect || this.singleSelect){
20460             if(this.multiSelect && e.shiftKey && this.lastSelection){
20461                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20462             }else{
20463                 this.select(item, this.multiSelect && e.ctrlKey);
20464                 this.lastSelection = item;
20465             }
20466             
20467             if(!this.tickable){
20468                 e.preventDefault();
20469             }
20470             
20471         }
20472         return true;
20473     },
20474
20475     /**
20476      * Get the number of selected nodes.
20477      * @return {Number}
20478      */
20479     getSelectionCount : function(){
20480         return this.selections.length;
20481     },
20482
20483     /**
20484      * Get the currently selected nodes.
20485      * @return {Array} An array of HTMLElements
20486      */
20487     getSelectedNodes : function(){
20488         return this.selections;
20489     },
20490
20491     /**
20492      * Get the indexes of the selected nodes.
20493      * @return {Array}
20494      */
20495     getSelectedIndexes : function(){
20496         var indexes = [], s = this.selections;
20497         for(var i = 0, len = s.length; i < len; i++){
20498             indexes.push(s[i].nodeIndex);
20499         }
20500         return indexes;
20501     },
20502
20503     /**
20504      * Clear all selections
20505      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20506      */
20507     clearSelections : function(suppressEvent){
20508         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20509             this.cmp.elements = this.selections;
20510             this.cmp.removeClass(this.selectedClass);
20511             this.selections = [];
20512             if(!suppressEvent){
20513                 this.fireEvent("selectionchange", this, this.selections);
20514             }
20515         }
20516     },
20517
20518     /**
20519      * Returns true if the passed node is selected
20520      * @param {HTMLElement/Number} node The node or node index
20521      * @return {Boolean}
20522      */
20523     isSelected : function(node){
20524         var s = this.selections;
20525         if(s.length < 1){
20526             return false;
20527         }
20528         node = this.getNode(node);
20529         return s.indexOf(node) !== -1;
20530     },
20531
20532     /**
20533      * Selects nodes.
20534      * @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
20535      * @param {Boolean} keepExisting (optional) true to keep existing selections
20536      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20537      */
20538     select : function(nodeInfo, keepExisting, suppressEvent){
20539         if(nodeInfo instanceof Array){
20540             if(!keepExisting){
20541                 this.clearSelections(true);
20542             }
20543             for(var i = 0, len = nodeInfo.length; i < len; i++){
20544                 this.select(nodeInfo[i], true, true);
20545             }
20546             return;
20547         } 
20548         var node = this.getNode(nodeInfo);
20549         if(!node || this.isSelected(node)){
20550             return; // already selected.
20551         }
20552         if(!keepExisting){
20553             this.clearSelections(true);
20554         }
20555         
20556         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20557             Roo.fly(node).addClass(this.selectedClass);
20558             this.selections.push(node);
20559             if(!suppressEvent){
20560                 this.fireEvent("selectionchange", this, this.selections);
20561             }
20562         }
20563         
20564         
20565     },
20566       /**
20567      * Unselects nodes.
20568      * @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
20569      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20570      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20571      */
20572     unselect : function(nodeInfo, keepExisting, suppressEvent)
20573     {
20574         if(nodeInfo instanceof Array){
20575             Roo.each(this.selections, function(s) {
20576                 this.unselect(s, nodeInfo);
20577             }, this);
20578             return;
20579         }
20580         var node = this.getNode(nodeInfo);
20581         if(!node || !this.isSelected(node)){
20582             //Roo.log("not selected");
20583             return; // not selected.
20584         }
20585         // fireevent???
20586         var ns = [];
20587         Roo.each(this.selections, function(s) {
20588             if (s == node ) {
20589                 Roo.fly(node).removeClass(this.selectedClass);
20590
20591                 return;
20592             }
20593             ns.push(s);
20594         },this);
20595         
20596         this.selections= ns;
20597         this.fireEvent("selectionchange", this, this.selections);
20598     },
20599
20600     /**
20601      * Gets a template node.
20602      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20603      * @return {HTMLElement} The node or null if it wasn't found
20604      */
20605     getNode : function(nodeInfo){
20606         if(typeof nodeInfo == "string"){
20607             return document.getElementById(nodeInfo);
20608         }else if(typeof nodeInfo == "number"){
20609             return this.nodes[nodeInfo];
20610         }
20611         return nodeInfo;
20612     },
20613
20614     /**
20615      * Gets a range template nodes.
20616      * @param {Number} startIndex
20617      * @param {Number} endIndex
20618      * @return {Array} An array of nodes
20619      */
20620     getNodes : function(start, end){
20621         var ns = this.nodes;
20622         start = start || 0;
20623         end = typeof end == "undefined" ? ns.length - 1 : end;
20624         var nodes = [];
20625         if(start <= end){
20626             for(var i = start; i <= end; i++){
20627                 nodes.push(ns[i]);
20628             }
20629         } else{
20630             for(var i = start; i >= end; i--){
20631                 nodes.push(ns[i]);
20632             }
20633         }
20634         return nodes;
20635     },
20636
20637     /**
20638      * Finds the index of the passed node
20639      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20640      * @return {Number} The index of the node or -1
20641      */
20642     indexOf : function(node){
20643         node = this.getNode(node);
20644         if(typeof node.nodeIndex == "number"){
20645             return node.nodeIndex;
20646         }
20647         var ns = this.nodes;
20648         for(var i = 0, len = ns.length; i < len; i++){
20649             if(ns[i] == node){
20650                 return i;
20651             }
20652         }
20653         return -1;
20654     }
20655 });
20656 /*
20657  * - LGPL
20658  *
20659  * based on jquery fullcalendar
20660  * 
20661  */
20662
20663 Roo.bootstrap = Roo.bootstrap || {};
20664 /**
20665  * @class Roo.bootstrap.Calendar
20666  * @extends Roo.bootstrap.Component
20667  * Bootstrap Calendar class
20668  * @cfg {Boolean} loadMask (true|false) default false
20669  * @cfg {Object} header generate the user specific header of the calendar, default false
20670
20671  * @constructor
20672  * Create a new Container
20673  * @param {Object} config The config object
20674  */
20675
20676
20677
20678 Roo.bootstrap.Calendar = function(config){
20679     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20680      this.addEvents({
20681         /**
20682              * @event select
20683              * Fires when a date is selected
20684              * @param {DatePicker} this
20685              * @param {Date} date The selected date
20686              */
20687         'select': true,
20688         /**
20689              * @event monthchange
20690              * Fires when the displayed month changes 
20691              * @param {DatePicker} this
20692              * @param {Date} date The selected month
20693              */
20694         'monthchange': true,
20695         /**
20696              * @event evententer
20697              * Fires when mouse over an event
20698              * @param {Calendar} this
20699              * @param {event} Event
20700              */
20701         'evententer': true,
20702         /**
20703              * @event eventleave
20704              * Fires when the mouse leaves an
20705              * @param {Calendar} this
20706              * @param {event}
20707              */
20708         'eventleave': true,
20709         /**
20710              * @event eventclick
20711              * Fires when the mouse click an
20712              * @param {Calendar} this
20713              * @param {event}
20714              */
20715         'eventclick': true
20716         
20717     });
20718
20719 };
20720
20721 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20722     
20723           /**
20724      * @cfg {Roo.data.Store} store
20725      * The data source for the calendar
20726      */
20727         store : false,
20728      /**
20729      * @cfg {Number} startDay
20730      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20731      */
20732     startDay : 0,
20733     
20734     loadMask : false,
20735     
20736     header : false,
20737       
20738     getAutoCreate : function(){
20739         
20740         
20741         var fc_button = function(name, corner, style, content ) {
20742             return Roo.apply({},{
20743                 tag : 'span',
20744                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20745                          (corner.length ?
20746                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20747                             ''
20748                         ),
20749                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20750                 unselectable: 'on'
20751             });
20752         };
20753         
20754         var header = {};
20755         
20756         if(!this.header){
20757             header = {
20758                 tag : 'table',
20759                 cls : 'fc-header',
20760                 style : 'width:100%',
20761                 cn : [
20762                     {
20763                         tag: 'tr',
20764                         cn : [
20765                             {
20766                                 tag : 'td',
20767                                 cls : 'fc-header-left',
20768                                 cn : [
20769                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20770                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20771                                     { tag: 'span', cls: 'fc-header-space' },
20772                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20773
20774
20775                                 ]
20776                             },
20777
20778                             {
20779                                 tag : 'td',
20780                                 cls : 'fc-header-center',
20781                                 cn : [
20782                                     {
20783                                         tag: 'span',
20784                                         cls: 'fc-header-title',
20785                                         cn : {
20786                                             tag: 'H2',
20787                                             html : 'month / year'
20788                                         }
20789                                     }
20790
20791                                 ]
20792                             },
20793                             {
20794                                 tag : 'td',
20795                                 cls : 'fc-header-right',
20796                                 cn : [
20797                               /*      fc_button('month', 'left', '', 'month' ),
20798                                     fc_button('week', '', '', 'week' ),
20799                                     fc_button('day', 'right', '', 'day' )
20800                                 */    
20801
20802                                 ]
20803                             }
20804
20805                         ]
20806                     }
20807                 ]
20808             };
20809         }
20810         
20811         header = this.header;
20812         
20813        
20814         var cal_heads = function() {
20815             var ret = [];
20816             // fixme - handle this.
20817             
20818             for (var i =0; i < Date.dayNames.length; i++) {
20819                 var d = Date.dayNames[i];
20820                 ret.push({
20821                     tag: 'th',
20822                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20823                     html : d.substring(0,3)
20824                 });
20825                 
20826             }
20827             ret[0].cls += ' fc-first';
20828             ret[6].cls += ' fc-last';
20829             return ret;
20830         };
20831         var cal_cell = function(n) {
20832             return  {
20833                 tag: 'td',
20834                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20835                 cn : [
20836                     {
20837                         cn : [
20838                             {
20839                                 cls: 'fc-day-number',
20840                                 html: 'D'
20841                             },
20842                             {
20843                                 cls: 'fc-day-content',
20844                              
20845                                 cn : [
20846                                      {
20847                                         style: 'position: relative;' // height: 17px;
20848                                     }
20849                                 ]
20850                             }
20851                             
20852                             
20853                         ]
20854                     }
20855                 ]
20856                 
20857             }
20858         };
20859         var cal_rows = function() {
20860             
20861             var ret = [];
20862             for (var r = 0; r < 6; r++) {
20863                 var row= {
20864                     tag : 'tr',
20865                     cls : 'fc-week',
20866                     cn : []
20867                 };
20868                 
20869                 for (var i =0; i < Date.dayNames.length; i++) {
20870                     var d = Date.dayNames[i];
20871                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20872
20873                 }
20874                 row.cn[0].cls+=' fc-first';
20875                 row.cn[0].cn[0].style = 'min-height:90px';
20876                 row.cn[6].cls+=' fc-last';
20877                 ret.push(row);
20878                 
20879             }
20880             ret[0].cls += ' fc-first';
20881             ret[4].cls += ' fc-prev-last';
20882             ret[5].cls += ' fc-last';
20883             return ret;
20884             
20885         };
20886         
20887         var cal_table = {
20888             tag: 'table',
20889             cls: 'fc-border-separate',
20890             style : 'width:100%',
20891             cellspacing  : 0,
20892             cn : [
20893                 { 
20894                     tag: 'thead',
20895                     cn : [
20896                         { 
20897                             tag: 'tr',
20898                             cls : 'fc-first fc-last',
20899                             cn : cal_heads()
20900                         }
20901                     ]
20902                 },
20903                 { 
20904                     tag: 'tbody',
20905                     cn : cal_rows()
20906                 }
20907                   
20908             ]
20909         };
20910          
20911          var cfg = {
20912             cls : 'fc fc-ltr',
20913             cn : [
20914                 header,
20915                 {
20916                     cls : 'fc-content',
20917                     style : "position: relative;",
20918                     cn : [
20919                         {
20920                             cls : 'fc-view fc-view-month fc-grid',
20921                             style : 'position: relative',
20922                             unselectable : 'on',
20923                             cn : [
20924                                 {
20925                                     cls : 'fc-event-container',
20926                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20927                                 },
20928                                 cal_table
20929                             ]
20930                         }
20931                     ]
20932     
20933                 }
20934            ] 
20935             
20936         };
20937         
20938          
20939         
20940         return cfg;
20941     },
20942     
20943     
20944     initEvents : function()
20945     {
20946         if(!this.store){
20947             throw "can not find store for calendar";
20948         }
20949         
20950         var mark = {
20951             tag: "div",
20952             cls:"x-dlg-mask",
20953             style: "text-align:center",
20954             cn: [
20955                 {
20956                     tag: "div",
20957                     style: "background-color:white;width:50%;margin:250 auto",
20958                     cn: [
20959                         {
20960                             tag: "img",
20961                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20962                         },
20963                         {
20964                             tag: "span",
20965                             html: "Loading"
20966                         }
20967                         
20968                     ]
20969                 }
20970             ]
20971         };
20972         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20973         
20974         var size = this.el.select('.fc-content', true).first().getSize();
20975         this.maskEl.setSize(size.width, size.height);
20976         this.maskEl.enableDisplayMode("block");
20977         if(!this.loadMask){
20978             this.maskEl.hide();
20979         }
20980         
20981         this.store = Roo.factory(this.store, Roo.data);
20982         this.store.on('load', this.onLoad, this);
20983         this.store.on('beforeload', this.onBeforeLoad, this);
20984         
20985         this.resize();
20986         
20987         this.cells = this.el.select('.fc-day',true);
20988         //Roo.log(this.cells);
20989         this.textNodes = this.el.query('.fc-day-number');
20990         this.cells.addClassOnOver('fc-state-hover');
20991         
20992         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20993         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20994         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20995         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20996         
20997         this.on('monthchange', this.onMonthChange, this);
20998         
20999         this.update(new Date().clearTime());
21000     },
21001     
21002     resize : function() {
21003         var sz  = this.el.getSize();
21004         
21005         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
21006         this.el.select('.fc-day-content div',true).setHeight(34);
21007     },
21008     
21009     
21010     // private
21011     showPrevMonth : function(e){
21012         this.update(this.activeDate.add("mo", -1));
21013     },
21014     showToday : function(e){
21015         this.update(new Date().clearTime());
21016     },
21017     // private
21018     showNextMonth : function(e){
21019         this.update(this.activeDate.add("mo", 1));
21020     },
21021
21022     // private
21023     showPrevYear : function(){
21024         this.update(this.activeDate.add("y", -1));
21025     },
21026
21027     // private
21028     showNextYear : function(){
21029         this.update(this.activeDate.add("y", 1));
21030     },
21031
21032     
21033    // private
21034     update : function(date)
21035     {
21036         var vd = this.activeDate;
21037         this.activeDate = date;
21038 //        if(vd && this.el){
21039 //            var t = date.getTime();
21040 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21041 //                Roo.log('using add remove');
21042 //                
21043 //                this.fireEvent('monthchange', this, date);
21044 //                
21045 //                this.cells.removeClass("fc-state-highlight");
21046 //                this.cells.each(function(c){
21047 //                   if(c.dateValue == t){
21048 //                       c.addClass("fc-state-highlight");
21049 //                       setTimeout(function(){
21050 //                            try{c.dom.firstChild.focus();}catch(e){}
21051 //                       }, 50);
21052 //                       return false;
21053 //                   }
21054 //                   return true;
21055 //                });
21056 //                return;
21057 //            }
21058 //        }
21059         
21060         var days = date.getDaysInMonth();
21061         
21062         var firstOfMonth = date.getFirstDateOfMonth();
21063         var startingPos = firstOfMonth.getDay()-this.startDay;
21064         
21065         if(startingPos < this.startDay){
21066             startingPos += 7;
21067         }
21068         
21069         var pm = date.add(Date.MONTH, -1);
21070         var prevStart = pm.getDaysInMonth()-startingPos;
21071 //        
21072         this.cells = this.el.select('.fc-day',true);
21073         this.textNodes = this.el.query('.fc-day-number');
21074         this.cells.addClassOnOver('fc-state-hover');
21075         
21076         var cells = this.cells.elements;
21077         var textEls = this.textNodes;
21078         
21079         Roo.each(cells, function(cell){
21080             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21081         });
21082         
21083         days += startingPos;
21084
21085         // convert everything to numbers so it's fast
21086         var day = 86400000;
21087         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21088         //Roo.log(d);
21089         //Roo.log(pm);
21090         //Roo.log(prevStart);
21091         
21092         var today = new Date().clearTime().getTime();
21093         var sel = date.clearTime().getTime();
21094         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21095         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21096         var ddMatch = this.disabledDatesRE;
21097         var ddText = this.disabledDatesText;
21098         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21099         var ddaysText = this.disabledDaysText;
21100         var format = this.format;
21101         
21102         var setCellClass = function(cal, cell){
21103             cell.row = 0;
21104             cell.events = [];
21105             cell.more = [];
21106             //Roo.log('set Cell Class');
21107             cell.title = "";
21108             var t = d.getTime();
21109             
21110             //Roo.log(d);
21111             
21112             cell.dateValue = t;
21113             if(t == today){
21114                 cell.className += " fc-today";
21115                 cell.className += " fc-state-highlight";
21116                 cell.title = cal.todayText;
21117             }
21118             if(t == sel){
21119                 // disable highlight in other month..
21120                 //cell.className += " fc-state-highlight";
21121                 
21122             }
21123             // disabling
21124             if(t < min) {
21125                 cell.className = " fc-state-disabled";
21126                 cell.title = cal.minText;
21127                 return;
21128             }
21129             if(t > max) {
21130                 cell.className = " fc-state-disabled";
21131                 cell.title = cal.maxText;
21132                 return;
21133             }
21134             if(ddays){
21135                 if(ddays.indexOf(d.getDay()) != -1){
21136                     cell.title = ddaysText;
21137                     cell.className = " fc-state-disabled";
21138                 }
21139             }
21140             if(ddMatch && format){
21141                 var fvalue = d.dateFormat(format);
21142                 if(ddMatch.test(fvalue)){
21143                     cell.title = ddText.replace("%0", fvalue);
21144                     cell.className = " fc-state-disabled";
21145                 }
21146             }
21147             
21148             if (!cell.initialClassName) {
21149                 cell.initialClassName = cell.dom.className;
21150             }
21151             
21152             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21153         };
21154
21155         var i = 0;
21156         
21157         for(; i < startingPos; i++) {
21158             textEls[i].innerHTML = (++prevStart);
21159             d.setDate(d.getDate()+1);
21160             
21161             cells[i].className = "fc-past fc-other-month";
21162             setCellClass(this, cells[i]);
21163         }
21164         
21165         var intDay = 0;
21166         
21167         for(; i < days; i++){
21168             intDay = i - startingPos + 1;
21169             textEls[i].innerHTML = (intDay);
21170             d.setDate(d.getDate()+1);
21171             
21172             cells[i].className = ''; // "x-date-active";
21173             setCellClass(this, cells[i]);
21174         }
21175         var extraDays = 0;
21176         
21177         for(; i < 42; i++) {
21178             textEls[i].innerHTML = (++extraDays);
21179             d.setDate(d.getDate()+1);
21180             
21181             cells[i].className = "fc-future fc-other-month";
21182             setCellClass(this, cells[i]);
21183         }
21184         
21185         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21186         
21187         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21188         
21189         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21190         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21191         
21192         if(totalRows != 6){
21193             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21194             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21195         }
21196         
21197         this.fireEvent('monthchange', this, date);
21198         
21199         
21200         /*
21201         if(!this.internalRender){
21202             var main = this.el.dom.firstChild;
21203             var w = main.offsetWidth;
21204             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21205             Roo.fly(main).setWidth(w);
21206             this.internalRender = true;
21207             // opera does not respect the auto grow header center column
21208             // then, after it gets a width opera refuses to recalculate
21209             // without a second pass
21210             if(Roo.isOpera && !this.secondPass){
21211                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21212                 this.secondPass = true;
21213                 this.update.defer(10, this, [date]);
21214             }
21215         }
21216         */
21217         
21218     },
21219     
21220     findCell : function(dt) {
21221         dt = dt.clearTime().getTime();
21222         var ret = false;
21223         this.cells.each(function(c){
21224             //Roo.log("check " +c.dateValue + '?=' + dt);
21225             if(c.dateValue == dt){
21226                 ret = c;
21227                 return false;
21228             }
21229             return true;
21230         });
21231         
21232         return ret;
21233     },
21234     
21235     findCells : function(ev) {
21236         var s = ev.start.clone().clearTime().getTime();
21237        // Roo.log(s);
21238         var e= ev.end.clone().clearTime().getTime();
21239        // Roo.log(e);
21240         var ret = [];
21241         this.cells.each(function(c){
21242              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21243             
21244             if(c.dateValue > e){
21245                 return ;
21246             }
21247             if(c.dateValue < s){
21248                 return ;
21249             }
21250             ret.push(c);
21251         });
21252         
21253         return ret;    
21254     },
21255     
21256 //    findBestRow: function(cells)
21257 //    {
21258 //        var ret = 0;
21259 //        
21260 //        for (var i =0 ; i < cells.length;i++) {
21261 //            ret  = Math.max(cells[i].rows || 0,ret);
21262 //        }
21263 //        return ret;
21264 //        
21265 //    },
21266     
21267     
21268     addItem : function(ev)
21269     {
21270         // look for vertical location slot in
21271         var cells = this.findCells(ev);
21272         
21273 //        ev.row = this.findBestRow(cells);
21274         
21275         // work out the location.
21276         
21277         var crow = false;
21278         var rows = [];
21279         for(var i =0; i < cells.length; i++) {
21280             
21281             cells[i].row = cells[0].row;
21282             
21283             if(i == 0){
21284                 cells[i].row = cells[i].row + 1;
21285             }
21286             
21287             if (!crow) {
21288                 crow = {
21289                     start : cells[i],
21290                     end :  cells[i]
21291                 };
21292                 continue;
21293             }
21294             if (crow.start.getY() == cells[i].getY()) {
21295                 // on same row.
21296                 crow.end = cells[i];
21297                 continue;
21298             }
21299             // different row.
21300             rows.push(crow);
21301             crow = {
21302                 start: cells[i],
21303                 end : cells[i]
21304             };
21305             
21306         }
21307         
21308         rows.push(crow);
21309         ev.els = [];
21310         ev.rows = rows;
21311         ev.cells = cells;
21312         
21313         cells[0].events.push(ev);
21314         
21315         this.calevents.push(ev);
21316     },
21317     
21318     clearEvents: function() {
21319         
21320         if(!this.calevents){
21321             return;
21322         }
21323         
21324         Roo.each(this.cells.elements, function(c){
21325             c.row = 0;
21326             c.events = [];
21327             c.more = [];
21328         });
21329         
21330         Roo.each(this.calevents, function(e) {
21331             Roo.each(e.els, function(el) {
21332                 el.un('mouseenter' ,this.onEventEnter, this);
21333                 el.un('mouseleave' ,this.onEventLeave, this);
21334                 el.remove();
21335             },this);
21336         },this);
21337         
21338         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21339             e.remove();
21340         });
21341         
21342     },
21343     
21344     renderEvents: function()
21345     {   
21346         var _this = this;
21347         
21348         this.cells.each(function(c) {
21349             
21350             if(c.row < 5){
21351                 return;
21352             }
21353             
21354             var ev = c.events;
21355             
21356             var r = 4;
21357             if(c.row != c.events.length){
21358                 r = 4 - (4 - (c.row - c.events.length));
21359             }
21360             
21361             c.events = ev.slice(0, r);
21362             c.more = ev.slice(r);
21363             
21364             if(c.more.length && c.more.length == 1){
21365                 c.events.push(c.more.pop());
21366             }
21367             
21368             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21369             
21370         });
21371             
21372         this.cells.each(function(c) {
21373             
21374             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21375             
21376             
21377             for (var e = 0; e < c.events.length; e++){
21378                 var ev = c.events[e];
21379                 var rows = ev.rows;
21380                 
21381                 for(var i = 0; i < rows.length; i++) {
21382                 
21383                     // how many rows should it span..
21384
21385                     var  cfg = {
21386                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21387                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21388
21389                         unselectable : "on",
21390                         cn : [
21391                             {
21392                                 cls: 'fc-event-inner',
21393                                 cn : [
21394     //                                {
21395     //                                  tag:'span',
21396     //                                  cls: 'fc-event-time',
21397     //                                  html : cells.length > 1 ? '' : ev.time
21398     //                                },
21399                                     {
21400                                       tag:'span',
21401                                       cls: 'fc-event-title',
21402                                       html : String.format('{0}', ev.title)
21403                                     }
21404
21405
21406                                 ]
21407                             },
21408                             {
21409                                 cls: 'ui-resizable-handle ui-resizable-e',
21410                                 html : '&nbsp;&nbsp;&nbsp'
21411                             }
21412
21413                         ]
21414                     };
21415
21416                     if (i == 0) {
21417                         cfg.cls += ' fc-event-start';
21418                     }
21419                     if ((i+1) == rows.length) {
21420                         cfg.cls += ' fc-event-end';
21421                     }
21422
21423                     var ctr = _this.el.select('.fc-event-container',true).first();
21424                     var cg = ctr.createChild(cfg);
21425
21426                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21427                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21428
21429                     var r = (c.more.length) ? 1 : 0;
21430                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21431                     cg.setWidth(ebox.right - sbox.x -2);
21432
21433                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21434                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21435                     cg.on('click', _this.onEventClick, _this, ev);
21436
21437                     ev.els.push(cg);
21438                     
21439                 }
21440                 
21441             }
21442             
21443             
21444             if(c.more.length){
21445                 var  cfg = {
21446                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21447                     style : 'position: absolute',
21448                     unselectable : "on",
21449                     cn : [
21450                         {
21451                             cls: 'fc-event-inner',
21452                             cn : [
21453                                 {
21454                                   tag:'span',
21455                                   cls: 'fc-event-title',
21456                                   html : 'More'
21457                                 }
21458
21459
21460                             ]
21461                         },
21462                         {
21463                             cls: 'ui-resizable-handle ui-resizable-e',
21464                             html : '&nbsp;&nbsp;&nbsp'
21465                         }
21466
21467                     ]
21468                 };
21469
21470                 var ctr = _this.el.select('.fc-event-container',true).first();
21471                 var cg = ctr.createChild(cfg);
21472
21473                 var sbox = c.select('.fc-day-content',true).first().getBox();
21474                 var ebox = c.select('.fc-day-content',true).first().getBox();
21475                 //Roo.log(cg);
21476                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21477                 cg.setWidth(ebox.right - sbox.x -2);
21478
21479                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21480                 
21481             }
21482             
21483         });
21484         
21485         
21486         
21487     },
21488     
21489     onEventEnter: function (e, el,event,d) {
21490         this.fireEvent('evententer', this, el, event);
21491     },
21492     
21493     onEventLeave: function (e, el,event,d) {
21494         this.fireEvent('eventleave', this, el, event);
21495     },
21496     
21497     onEventClick: function (e, el,event,d) {
21498         this.fireEvent('eventclick', this, el, event);
21499     },
21500     
21501     onMonthChange: function () {
21502         this.store.load();
21503     },
21504     
21505     onMoreEventClick: function(e, el, more)
21506     {
21507         var _this = this;
21508         
21509         this.calpopover.placement = 'right';
21510         this.calpopover.setTitle('More');
21511         
21512         this.calpopover.setContent('');
21513         
21514         var ctr = this.calpopover.el.select('.popover-content', true).first();
21515         
21516         Roo.each(more, function(m){
21517             var cfg = {
21518                 cls : 'fc-event-hori fc-event-draggable',
21519                 html : m.title
21520             };
21521             var cg = ctr.createChild(cfg);
21522             
21523             cg.on('click', _this.onEventClick, _this, m);
21524         });
21525         
21526         this.calpopover.show(el);
21527         
21528         
21529     },
21530     
21531     onLoad: function () 
21532     {   
21533         this.calevents = [];
21534         var cal = this;
21535         
21536         if(this.store.getCount() > 0){
21537             this.store.data.each(function(d){
21538                cal.addItem({
21539                     id : d.data.id,
21540                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21541                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21542                     time : d.data.start_time,
21543                     title : d.data.title,
21544                     description : d.data.description,
21545                     venue : d.data.venue
21546                 });
21547             });
21548         }
21549         
21550         this.renderEvents();
21551         
21552         if(this.calevents.length && this.loadMask){
21553             this.maskEl.hide();
21554         }
21555     },
21556     
21557     onBeforeLoad: function()
21558     {
21559         this.clearEvents();
21560         if(this.loadMask){
21561             this.maskEl.show();
21562         }
21563     }
21564 });
21565
21566  
21567  /*
21568  * - LGPL
21569  *
21570  * element
21571  * 
21572  */
21573
21574 /**
21575  * @class Roo.bootstrap.Popover
21576  * @extends Roo.bootstrap.Component
21577  * @parent none builder
21578  * @children Roo.bootstrap.Component
21579  * Bootstrap Popover class
21580  * @cfg {String} html contents of the popover   (or false to use children..)
21581  * @cfg {String} title of popover (or false to hide)
21582  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21583  * @cfg {String} trigger click || hover (or false to trigger manually)
21584  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21585  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21586  *      - if false and it has a 'parent' then it will be automatically added to that element
21587  *      - if string - Roo.get  will be called 
21588  * @cfg {Number} delay - delay before showing
21589  
21590  * @constructor
21591  * Create a new Popover
21592  * @param {Object} config The config object
21593  */
21594
21595 Roo.bootstrap.Popover = function(config){
21596     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21597     
21598     this.addEvents({
21599         // raw events
21600          /**
21601          * @event show
21602          * After the popover show
21603          * 
21604          * @param {Roo.bootstrap.Popover} this
21605          */
21606         "show" : true,
21607         /**
21608          * @event hide
21609          * After the popover hide
21610          * 
21611          * @param {Roo.bootstrap.Popover} this
21612          */
21613         "hide" : true
21614     });
21615 };
21616
21617 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21618     
21619     title: false,
21620     html: false,
21621     
21622     placement : 'right',
21623     trigger : 'hover', // hover
21624     modal : false,
21625     delay : 0,
21626     
21627     over: false,
21628     
21629     can_build_overlaid : false,
21630     
21631     maskEl : false, // the mask element
21632     headerEl : false,
21633     contentEl : false,
21634     alignEl : false, // when show is called with an element - this get's stored.
21635     
21636     getChildContainer : function()
21637     {
21638         return this.contentEl;
21639         
21640     },
21641     getPopoverHeader : function()
21642     {
21643         this.title = true; // flag not to hide it..
21644         this.headerEl.addClass('p-0');
21645         return this.headerEl
21646     },
21647     
21648     
21649     getAutoCreate : function(){
21650          
21651         var cfg = {
21652            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21653            style: 'display:block',
21654            cn : [
21655                 {
21656                     cls : 'arrow'
21657                 },
21658                 {
21659                     cls : 'popover-inner ',
21660                     cn : [
21661                         {
21662                             tag: 'h3',
21663                             cls: 'popover-title popover-header',
21664                             html : this.title === false ? '' : this.title
21665                         },
21666                         {
21667                             cls : 'popover-content popover-body '  + (this.cls || ''),
21668                             html : this.html || ''
21669                         }
21670                     ]
21671                     
21672                 }
21673            ]
21674         };
21675         
21676         return cfg;
21677     },
21678     /**
21679      * @param {string} the title
21680      */
21681     setTitle: function(str)
21682     {
21683         this.title = str;
21684         if (this.el) {
21685             this.headerEl.dom.innerHTML = str;
21686         }
21687         
21688     },
21689     /**
21690      * @param {string} the body content
21691      */
21692     setContent: function(str)
21693     {
21694         this.html = str;
21695         if (this.contentEl) {
21696             this.contentEl.dom.innerHTML = str;
21697         }
21698         
21699     },
21700     // as it get's added to the bottom of the page.
21701     onRender : function(ct, position)
21702     {
21703         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21704         
21705         
21706         
21707         if(!this.el){
21708             var cfg = Roo.apply({},  this.getAutoCreate());
21709             cfg.id = Roo.id();
21710             
21711             if (this.cls) {
21712                 cfg.cls += ' ' + this.cls;
21713             }
21714             if (this.style) {
21715                 cfg.style = this.style;
21716             }
21717             //Roo.log("adding to ");
21718             this.el = Roo.get(document.body).createChild(cfg, position);
21719 //            Roo.log(this.el);
21720         }
21721         
21722         this.contentEl = this.el.select('.popover-content',true).first();
21723         this.headerEl =  this.el.select('.popover-title',true).first();
21724         
21725         var nitems = [];
21726         if(typeof(this.items) != 'undefined'){
21727             var items = this.items;
21728             delete this.items;
21729
21730             for(var i =0;i < items.length;i++) {
21731                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21732             }
21733         }
21734
21735         this.items = nitems;
21736         
21737         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21738         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21739         
21740         
21741         
21742         this.initEvents();
21743     },
21744     
21745     resizeMask : function()
21746     {
21747         this.maskEl.setSize(
21748             Roo.lib.Dom.getViewWidth(true),
21749             Roo.lib.Dom.getViewHeight(true)
21750         );
21751     },
21752     
21753     initEvents : function()
21754     {
21755         
21756         if (!this.modal) { 
21757             Roo.bootstrap.Popover.register(this);
21758         }
21759          
21760         this.arrowEl = this.el.select('.arrow',true).first();
21761         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21762         this.el.enableDisplayMode('block');
21763         this.el.hide();
21764  
21765         
21766         if (this.over === false && !this.parent()) {
21767             return; 
21768         }
21769         if (this.triggers === false) {
21770             return;
21771         }
21772          
21773         // support parent
21774         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21775         var triggers = this.trigger ? this.trigger.split(' ') : [];
21776         Roo.each(triggers, function(trigger) {
21777         
21778             if (trigger == 'click') {
21779                 on_el.on('click', this.toggle, this);
21780             } else if (trigger != 'manual') {
21781                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21782                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21783       
21784                 on_el.on(eventIn  ,this.enter, this);
21785                 on_el.on(eventOut, this.leave, this);
21786             }
21787         }, this);
21788     },
21789     
21790     
21791     // private
21792     timeout : null,
21793     hoverState : null,
21794     
21795     toggle : function () {
21796         this.hoverState == 'in' ? this.leave() : this.enter();
21797     },
21798     
21799     enter : function () {
21800         
21801         clearTimeout(this.timeout);
21802     
21803         this.hoverState = 'in';
21804     
21805         if (!this.delay || !this.delay.show) {
21806             this.show();
21807             return;
21808         }
21809         var _t = this;
21810         this.timeout = setTimeout(function () {
21811             if (_t.hoverState == 'in') {
21812                 _t.show();
21813             }
21814         }, this.delay.show)
21815     },
21816     
21817     leave : function() {
21818         clearTimeout(this.timeout);
21819     
21820         this.hoverState = 'out';
21821     
21822         if (!this.delay || !this.delay.hide) {
21823             this.hide();
21824             return;
21825         }
21826         var _t = this;
21827         this.timeout = setTimeout(function () {
21828             if (_t.hoverState == 'out') {
21829                 _t.hide();
21830             }
21831         }, this.delay.hide)
21832     },
21833     
21834     /**
21835      * update the position of the dialog
21836      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21837      * 
21838      *
21839      */
21840     
21841     doAlign : function()
21842     {
21843         
21844         if (this.alignEl) {
21845             this.updatePosition(this.placement, true);
21846              
21847         } else {
21848             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21849             var es = this.el.getSize();
21850             var x = Roo.lib.Dom.getViewWidth()/2;
21851             var y = Roo.lib.Dom.getViewHeight()/2;
21852             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21853             
21854         }
21855
21856          
21857          
21858         
21859         
21860     },
21861     
21862     /**
21863      * Show the popover
21864      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21865      * @param {string} (left|right|top|bottom) position
21866      */
21867     show : function (on_el, placement)
21868     {
21869         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21870         on_el = on_el || false; // default to false
21871          
21872         if (!on_el) {
21873             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21874                 on_el = this.parent().el;
21875             } else if (this.over) {
21876                 on_el = Roo.get(this.over);
21877             }
21878             
21879         }
21880         
21881         this.alignEl = Roo.get( on_el );
21882
21883         if (!this.el) {
21884             this.render(document.body);
21885         }
21886         
21887         
21888          
21889         
21890         if (this.title === false) {
21891             this.headerEl.hide();
21892         }
21893         
21894        
21895         this.el.show();
21896         this.el.dom.style.display = 'block';
21897          
21898         this.doAlign();
21899         
21900         //var arrow = this.el.select('.arrow',true).first();
21901         //arrow.set(align[2], 
21902         
21903         this.el.addClass('in');
21904         
21905          
21906         
21907         this.hoverState = 'in';
21908         
21909         if (this.modal) {
21910             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21911             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21912             this.maskEl.dom.style.display = 'block';
21913             this.maskEl.addClass('show');
21914         }
21915         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21916  
21917         this.fireEvent('show', this);
21918         
21919     },
21920     /**
21921      * fire this manually after loading a grid in the table for example
21922      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21923      * @param {Boolean} try and move it if we cant get right position.
21924      */
21925     updatePosition : function(placement, try_move)
21926     {
21927         // allow for calling with no parameters
21928         placement = placement   ? placement :  this.placement;
21929         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21930         
21931         this.el.removeClass([
21932             'fade','top','bottom', 'left', 'right','in',
21933             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21934         ]);
21935         this.el.addClass(placement + ' bs-popover-' + placement);
21936         
21937         if (!this.alignEl ) {
21938             return false;
21939         }
21940         
21941         switch (placement) {
21942             case 'right':
21943                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21944                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21945                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21946                     //normal display... or moved up/down.
21947                     this.el.setXY(offset);
21948                     var xy = this.alignEl.getAnchorXY('tr', false);
21949                     xy[0]+=2;xy[1]+=5;
21950                     this.arrowEl.setXY(xy);
21951                     return true;
21952                 }
21953                 // continue through...
21954                 return this.updatePosition('left', false);
21955                 
21956             
21957             case 'left':
21958                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21959                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21960                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21961                     //normal display... or moved up/down.
21962                     this.el.setXY(offset);
21963                     var xy = this.alignEl.getAnchorXY('tl', false);
21964                     xy[0]-=10;xy[1]+=5; // << fix me
21965                     this.arrowEl.setXY(xy);
21966                     return true;
21967                 }
21968                 // call self...
21969                 return this.updatePosition('right', false);
21970             
21971             case 'top':
21972                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21973                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21974                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21975                     //normal display... or moved up/down.
21976                     this.el.setXY(offset);
21977                     var xy = this.alignEl.getAnchorXY('t', false);
21978                     xy[1]-=10; // << fix me
21979                     this.arrowEl.setXY(xy);
21980                     return true;
21981                 }
21982                 // fall through
21983                return this.updatePosition('bottom', false);
21984             
21985             case 'bottom':
21986                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21987                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21988                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21989                     //normal display... or moved up/down.
21990                     this.el.setXY(offset);
21991                     var xy = this.alignEl.getAnchorXY('b', false);
21992                      xy[1]+=2; // << fix me
21993                     this.arrowEl.setXY(xy);
21994                     return true;
21995                 }
21996                 // fall through
21997                 return this.updatePosition('top', false);
21998                 
21999             
22000         }
22001         
22002         
22003         return false;
22004     },
22005     
22006     hide : function()
22007     {
22008         this.el.setXY([0,0]);
22009         this.el.removeClass('in');
22010         this.el.hide();
22011         this.hoverState = null;
22012         this.maskEl.hide(); // always..
22013         this.fireEvent('hide', this);
22014     }
22015     
22016 });
22017
22018
22019 Roo.apply(Roo.bootstrap.Popover, {
22020
22021     alignment : {
22022         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22023         'right' : ['l-br', [10,0], 'right bs-popover-right'],
22024         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22025         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22026     },
22027     
22028     zIndex : 20001,
22029
22030     clickHander : false,
22031     
22032     
22033
22034     onMouseDown : function(e)
22035     {
22036         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22037             /// what is nothing is showing..
22038             this.hideAll();
22039         }
22040          
22041     },
22042     
22043     
22044     popups : [],
22045     
22046     register : function(popup)
22047     {
22048         if (!Roo.bootstrap.Popover.clickHandler) {
22049             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22050         }
22051         // hide other popups.
22052         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22053         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22054         this.hideAll(); //<< why?
22055         //this.popups.push(popup);
22056     },
22057     hideAll : function()
22058     {
22059         this.popups.forEach(function(p) {
22060             p.hide();
22061         });
22062     },
22063     onShow : function() {
22064         Roo.bootstrap.Popover.popups.push(this);
22065     },
22066     onHide : function() {
22067         Roo.bootstrap.Popover.popups.remove(this);
22068     } 
22069
22070 });
22071 /**
22072  * @class Roo.bootstrap.PopoverNav
22073  * @extends Roo.bootstrap.nav.Simplebar
22074  * @parent Roo.bootstrap.Popover
22075  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22076  * @licence LGPL
22077  * Bootstrap Popover header navigation class
22078  * FIXME? should this go under nav?
22079  *
22080  * 
22081  * @constructor
22082  * Create a new Popover Header Navigation 
22083  * @param {Object} config The config object
22084  */
22085
22086 Roo.bootstrap.PopoverNav = function(config){
22087     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22088 };
22089
22090 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22091     
22092     
22093     container_method : 'getPopoverHeader' 
22094     
22095      
22096     
22097     
22098    
22099 });
22100
22101  
22102
22103  /*
22104  * - LGPL
22105  *
22106  * Progress
22107  * 
22108  */
22109
22110 /**
22111  * @class Roo.bootstrap.Progress
22112  * @extends Roo.bootstrap.Component
22113  * @children Roo.bootstrap.ProgressBar
22114  * Bootstrap Progress class
22115  * @cfg {Boolean} striped striped of the progress bar
22116  * @cfg {Boolean} active animated of the progress bar
22117  * 
22118  * 
22119  * @constructor
22120  * Create a new Progress
22121  * @param {Object} config The config object
22122  */
22123
22124 Roo.bootstrap.Progress = function(config){
22125     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22126 };
22127
22128 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22129     
22130     striped : false,
22131     active: false,
22132     
22133     getAutoCreate : function(){
22134         var cfg = {
22135             tag: 'div',
22136             cls: 'progress'
22137         };
22138         
22139         
22140         if(this.striped){
22141             cfg.cls += ' progress-striped';
22142         }
22143       
22144         if(this.active){
22145             cfg.cls += ' active';
22146         }
22147         
22148         
22149         return cfg;
22150     }
22151    
22152 });
22153
22154  
22155
22156  /*
22157  * - LGPL
22158  *
22159  * ProgressBar
22160  * 
22161  */
22162
22163 /**
22164  * @class Roo.bootstrap.ProgressBar
22165  * @extends Roo.bootstrap.Component
22166  * Bootstrap ProgressBar class
22167  * @cfg {Number} aria_valuenow aria-value now
22168  * @cfg {Number} aria_valuemin aria-value min
22169  * @cfg {Number} aria_valuemax aria-value max
22170  * @cfg {String} label label for the progress bar
22171  * @cfg {String} panel (success | info | warning | danger )
22172  * @cfg {String} role role of the progress bar
22173  * @cfg {String} sr_only text
22174  * 
22175  * 
22176  * @constructor
22177  * Create a new ProgressBar
22178  * @param {Object} config The config object
22179  */
22180
22181 Roo.bootstrap.ProgressBar = function(config){
22182     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22183 };
22184
22185 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22186     
22187     aria_valuenow : 0,
22188     aria_valuemin : 0,
22189     aria_valuemax : 100,
22190     label : false,
22191     panel : false,
22192     role : false,
22193     sr_only: false,
22194     
22195     getAutoCreate : function()
22196     {
22197         
22198         var cfg = {
22199             tag: 'div',
22200             cls: 'progress-bar',
22201             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22202         };
22203         
22204         if(this.sr_only){
22205             cfg.cn = {
22206                 tag: 'span',
22207                 cls: 'sr-only',
22208                 html: this.sr_only
22209             }
22210         }
22211         
22212         if(this.role){
22213             cfg.role = this.role;
22214         }
22215         
22216         if(this.aria_valuenow){
22217             cfg['aria-valuenow'] = this.aria_valuenow;
22218         }
22219         
22220         if(this.aria_valuemin){
22221             cfg['aria-valuemin'] = this.aria_valuemin;
22222         }
22223         
22224         if(this.aria_valuemax){
22225             cfg['aria-valuemax'] = this.aria_valuemax;
22226         }
22227         
22228         if(this.label && !this.sr_only){
22229             cfg.html = this.label;
22230         }
22231         
22232         if(this.panel){
22233             cfg.cls += ' progress-bar-' + this.panel;
22234         }
22235         
22236         return cfg;
22237     },
22238     
22239     update : function(aria_valuenow)
22240     {
22241         this.aria_valuenow = aria_valuenow;
22242         
22243         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22244     }
22245    
22246 });
22247
22248  
22249
22250  /**
22251  * @class Roo.bootstrap.TabGroup
22252  * @extends Roo.bootstrap.Column
22253  * @children Roo.bootstrap.TabPanel
22254  * Bootstrap Column class
22255  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22256  * @cfg {Boolean} carousel true to make the group behave like a carousel
22257  * @cfg {Boolean} bullets show bullets for the panels
22258  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22259  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22260  * @cfg {Boolean} showarrow (true|false) show arrow default true
22261  * 
22262  * @constructor
22263  * Create a new TabGroup
22264  * @param {Object} config The config object
22265  */
22266
22267 Roo.bootstrap.TabGroup = function(config){
22268     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22269     if (!this.navId) {
22270         this.navId = Roo.id();
22271     }
22272     this.tabs = [];
22273     Roo.bootstrap.TabGroup.register(this);
22274     
22275 };
22276
22277 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22278     
22279     carousel : false,
22280     transition : false,
22281     bullets : 0,
22282     timer : 0,
22283     autoslide : false,
22284     slideFn : false,
22285     slideOnTouch : false,
22286     showarrow : true,
22287     
22288     getAutoCreate : function()
22289     {
22290         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22291         
22292         cfg.cls += ' tab-content';
22293         
22294         if (this.carousel) {
22295             cfg.cls += ' carousel slide';
22296             
22297             cfg.cn = [{
22298                cls : 'carousel-inner',
22299                cn : []
22300             }];
22301         
22302             if(this.bullets  && !Roo.isTouch){
22303                 
22304                 var bullets = {
22305                     cls : 'carousel-bullets',
22306                     cn : []
22307                 };
22308                
22309                 if(this.bullets_cls){
22310                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22311                 }
22312                 
22313                 bullets.cn.push({
22314                     cls : 'clear'
22315                 });
22316                 
22317                 cfg.cn[0].cn.push(bullets);
22318             }
22319             
22320             if(this.showarrow){
22321                 cfg.cn[0].cn.push({
22322                     tag : 'div',
22323                     class : 'carousel-arrow',
22324                     cn : [
22325                         {
22326                             tag : 'div',
22327                             class : 'carousel-prev',
22328                             cn : [
22329                                 {
22330                                     tag : 'i',
22331                                     class : 'fa fa-chevron-left'
22332                                 }
22333                             ]
22334                         },
22335                         {
22336                             tag : 'div',
22337                             class : 'carousel-next',
22338                             cn : [
22339                                 {
22340                                     tag : 'i',
22341                                     class : 'fa fa-chevron-right'
22342                                 }
22343                             ]
22344                         }
22345                     ]
22346                 });
22347             }
22348             
22349         }
22350         
22351         return cfg;
22352     },
22353     
22354     initEvents:  function()
22355     {
22356 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22357 //            this.el.on("touchstart", this.onTouchStart, this);
22358 //        }
22359         
22360         if(this.autoslide){
22361             var _this = this;
22362             
22363             this.slideFn = window.setInterval(function() {
22364                 _this.showPanelNext();
22365             }, this.timer);
22366         }
22367         
22368         if(this.showarrow){
22369             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22370             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22371         }
22372         
22373         
22374     },
22375     
22376 //    onTouchStart : function(e, el, o)
22377 //    {
22378 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22379 //            return;
22380 //        }
22381 //        
22382 //        this.showPanelNext();
22383 //    },
22384     
22385     
22386     getChildContainer : function()
22387     {
22388         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22389     },
22390     
22391     /**
22392     * register a Navigation item
22393     * @param {Roo.bootstrap.nav.Item} the navitem to add
22394     */
22395     register : function(item)
22396     {
22397         this.tabs.push( item);
22398         item.navId = this.navId; // not really needed..
22399         this.addBullet();
22400     
22401     },
22402     
22403     getActivePanel : function()
22404     {
22405         var r = false;
22406         Roo.each(this.tabs, function(t) {
22407             if (t.active) {
22408                 r = t;
22409                 return false;
22410             }
22411             return null;
22412         });
22413         return r;
22414         
22415     },
22416     getPanelByName : function(n)
22417     {
22418         var r = false;
22419         Roo.each(this.tabs, function(t) {
22420             if (t.tabId == n) {
22421                 r = t;
22422                 return false;
22423             }
22424             return null;
22425         });
22426         return r;
22427     },
22428     indexOfPanel : function(p)
22429     {
22430         var r = false;
22431         Roo.each(this.tabs, function(t,i) {
22432             if (t.tabId == p.tabId) {
22433                 r = i;
22434                 return false;
22435             }
22436             return null;
22437         });
22438         return r;
22439     },
22440     /**
22441      * show a specific panel
22442      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22443      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22444      */
22445     showPanel : function (pan)
22446     {
22447         if(this.transition || typeof(pan) == 'undefined'){
22448             Roo.log("waiting for the transitionend");
22449             return false;
22450         }
22451         
22452         if (typeof(pan) == 'number') {
22453             pan = this.tabs[pan];
22454         }
22455         
22456         if (typeof(pan) == 'string') {
22457             pan = this.getPanelByName(pan);
22458         }
22459         
22460         var cur = this.getActivePanel();
22461         
22462         if(!pan || !cur){
22463             Roo.log('pan or acitve pan is undefined');
22464             return false;
22465         }
22466         
22467         if (pan.tabId == this.getActivePanel().tabId) {
22468             return true;
22469         }
22470         
22471         if (false === cur.fireEvent('beforedeactivate')) {
22472             return false;
22473         }
22474         
22475         if(this.bullets > 0 && !Roo.isTouch){
22476             this.setActiveBullet(this.indexOfPanel(pan));
22477         }
22478         
22479         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22480             
22481             //class="carousel-item carousel-item-next carousel-item-left"
22482             
22483             this.transition = true;
22484             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22485             var lr = dir == 'next' ? 'left' : 'right';
22486             pan.el.addClass(dir); // or prev
22487             pan.el.addClass('carousel-item-' + dir); // or prev
22488             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22489             cur.el.addClass(lr); // or right
22490             pan.el.addClass(lr);
22491             cur.el.addClass('carousel-item-' +lr); // or right
22492             pan.el.addClass('carousel-item-' +lr);
22493             
22494             
22495             var _this = this;
22496             cur.el.on('transitionend', function() {
22497                 Roo.log("trans end?");
22498                 
22499                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22500                 pan.setActive(true);
22501                 
22502                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22503                 cur.setActive(false);
22504                 
22505                 _this.transition = false;
22506                 
22507             }, this, { single:  true } );
22508             
22509             return true;
22510         }
22511         
22512         cur.setActive(false);
22513         pan.setActive(true);
22514         
22515         return true;
22516         
22517     },
22518     showPanelNext : function()
22519     {
22520         var i = this.indexOfPanel(this.getActivePanel());
22521         
22522         if (i >= this.tabs.length - 1 && !this.autoslide) {
22523             return;
22524         }
22525         
22526         if (i >= this.tabs.length - 1 && this.autoslide) {
22527             i = -1;
22528         }
22529         
22530         this.showPanel(this.tabs[i+1]);
22531     },
22532     
22533     showPanelPrev : function()
22534     {
22535         var i = this.indexOfPanel(this.getActivePanel());
22536         
22537         if (i  < 1 && !this.autoslide) {
22538             return;
22539         }
22540         
22541         if (i < 1 && this.autoslide) {
22542             i = this.tabs.length;
22543         }
22544         
22545         this.showPanel(this.tabs[i-1]);
22546     },
22547     
22548     
22549     addBullet: function()
22550     {
22551         if(!this.bullets || Roo.isTouch){
22552             return;
22553         }
22554         var ctr = this.el.select('.carousel-bullets',true).first();
22555         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22556         var bullet = ctr.createChild({
22557             cls : 'bullet bullet-' + i
22558         },ctr.dom.lastChild);
22559         
22560         
22561         var _this = this;
22562         
22563         bullet.on('click', (function(e, el, o, ii, t){
22564
22565             e.preventDefault();
22566
22567             this.showPanel(ii);
22568
22569             if(this.autoslide && this.slideFn){
22570                 clearInterval(this.slideFn);
22571                 this.slideFn = window.setInterval(function() {
22572                     _this.showPanelNext();
22573                 }, this.timer);
22574             }
22575
22576         }).createDelegate(this, [i, bullet], true));
22577                 
22578         
22579     },
22580      
22581     setActiveBullet : function(i)
22582     {
22583         if(Roo.isTouch){
22584             return;
22585         }
22586         
22587         Roo.each(this.el.select('.bullet', true).elements, function(el){
22588             el.removeClass('selected');
22589         });
22590
22591         var bullet = this.el.select('.bullet-' + i, true).first();
22592         
22593         if(!bullet){
22594             return;
22595         }
22596         
22597         bullet.addClass('selected');
22598     }
22599     
22600     
22601   
22602 });
22603
22604  
22605
22606  
22607  
22608 Roo.apply(Roo.bootstrap.TabGroup, {
22609     
22610     groups: {},
22611      /**
22612     * register a Navigation Group
22613     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22614     */
22615     register : function(navgrp)
22616     {
22617         this.groups[navgrp.navId] = navgrp;
22618         
22619     },
22620     /**
22621     * fetch a Navigation Group based on the navigation ID
22622     * if one does not exist , it will get created.
22623     * @param {string} the navgroup to add
22624     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22625     */
22626     get: function(navId) {
22627         if (typeof(this.groups[navId]) == 'undefined') {
22628             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22629         }
22630         return this.groups[navId] ;
22631     }
22632     
22633     
22634     
22635 });
22636
22637  /*
22638  * - LGPL
22639  *
22640  * TabPanel
22641  * 
22642  */
22643
22644 /**
22645  * @class Roo.bootstrap.TabPanel
22646  * @extends Roo.bootstrap.Component
22647  * @children Roo.bootstrap.Component
22648  * Bootstrap TabPanel class
22649  * @cfg {Boolean} active panel active
22650  * @cfg {String} html panel content
22651  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22652  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22653  * @cfg {String} href click to link..
22654  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22655  * 
22656  * 
22657  * @constructor
22658  * Create a new TabPanel
22659  * @param {Object} config The config object
22660  */
22661
22662 Roo.bootstrap.TabPanel = function(config){
22663     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22664     this.addEvents({
22665         /**
22666              * @event changed
22667              * Fires when the active status changes
22668              * @param {Roo.bootstrap.TabPanel} this
22669              * @param {Boolean} state the new state
22670             
22671          */
22672         'changed': true,
22673         /**
22674              * @event beforedeactivate
22675              * Fires before a tab is de-activated - can be used to do validation on a form.
22676              * @param {Roo.bootstrap.TabPanel} this
22677              * @return {Boolean} false if there is an error
22678             
22679          */
22680         'beforedeactivate': true
22681      });
22682     
22683     this.tabId = this.tabId || Roo.id();
22684   
22685 };
22686
22687 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22688     
22689     active: false,
22690     html: false,
22691     tabId: false,
22692     navId : false,
22693     href : '',
22694     touchSlide : false,
22695     getAutoCreate : function(){
22696         
22697         
22698         var cfg = {
22699             tag: 'div',
22700             // item is needed for carousel - not sure if it has any effect otherwise
22701             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22702             html: this.html || ''
22703         };
22704         
22705         if(this.active){
22706             cfg.cls += ' active';
22707         }
22708         
22709         if(this.tabId){
22710             cfg.tabId = this.tabId;
22711         }
22712         
22713         
22714         
22715         return cfg;
22716     },
22717     
22718     initEvents:  function()
22719     {
22720         var p = this.parent();
22721         
22722         this.navId = this.navId || p.navId;
22723         
22724         if (typeof(this.navId) != 'undefined') {
22725             // not really needed.. but just in case.. parent should be a NavGroup.
22726             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22727             
22728             tg.register(this);
22729             
22730             var i = tg.tabs.length - 1;
22731             
22732             if(this.active && tg.bullets > 0 && i < tg.bullets){
22733                 tg.setActiveBullet(i);
22734             }
22735         }
22736         
22737         this.el.on('click', this.onClick, this);
22738         
22739         if(Roo.isTouch && this.touchSlide){
22740             this.el.on("touchstart", this.onTouchStart, this);
22741             this.el.on("touchmove", this.onTouchMove, this);
22742             this.el.on("touchend", this.onTouchEnd, this);
22743         }
22744         
22745     },
22746     
22747     onRender : function(ct, position)
22748     {
22749         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22750     },
22751     
22752     setActive : function(state)
22753     {
22754         Roo.log("panel - set active " + this.tabId + "=" + state);
22755         
22756         this.active = state;
22757         if (!state) {
22758             this.el.removeClass('active');
22759             
22760         } else  if (!this.el.hasClass('active')) {
22761             this.el.addClass('active');
22762         }
22763         
22764         this.fireEvent('changed', this, state);
22765     },
22766     
22767     onClick : function(e)
22768     {
22769         e.preventDefault();
22770         
22771         if(!this.href.length){
22772             return;
22773         }
22774         
22775         window.location.href = this.href;
22776     },
22777     
22778     startX : 0,
22779     startY : 0,
22780     endX : 0,
22781     endY : 0,
22782     swiping : false,
22783     
22784     onTouchStart : function(e)
22785     {
22786         this.swiping = false;
22787         
22788         this.startX = e.browserEvent.touches[0].clientX;
22789         this.startY = e.browserEvent.touches[0].clientY;
22790     },
22791     
22792     onTouchMove : function(e)
22793     {
22794         this.swiping = true;
22795         
22796         this.endX = e.browserEvent.touches[0].clientX;
22797         this.endY = e.browserEvent.touches[0].clientY;
22798     },
22799     
22800     onTouchEnd : function(e)
22801     {
22802         if(!this.swiping){
22803             this.onClick(e);
22804             return;
22805         }
22806         
22807         var tabGroup = this.parent();
22808         
22809         if(this.endX > this.startX){ // swiping right
22810             tabGroup.showPanelPrev();
22811             return;
22812         }
22813         
22814         if(this.startX > this.endX){ // swiping left
22815             tabGroup.showPanelNext();
22816             return;
22817         }
22818     }
22819     
22820     
22821 });
22822  
22823
22824  
22825
22826  /*
22827  * - LGPL
22828  *
22829  * DateField
22830  * 
22831  */
22832
22833 /**
22834  * @class Roo.bootstrap.form.DateField
22835  * @extends Roo.bootstrap.form.Input
22836  * Bootstrap DateField class
22837  * @cfg {Number} weekStart default 0
22838  * @cfg {String} viewMode default empty, (months|years)
22839  * @cfg {String} minViewMode default empty, (months|years)
22840  * @cfg {Number} startDate default -Infinity
22841  * @cfg {Number} endDate default Infinity
22842  * @cfg {Boolean} todayHighlight default false
22843  * @cfg {Boolean} todayBtn default false
22844  * @cfg {Boolean} calendarWeeks default false
22845  * @cfg {Object} daysOfWeekDisabled default empty
22846  * @cfg {Boolean} singleMode default false (true | false)
22847  * 
22848  * @cfg {Boolean} keyboardNavigation default true
22849  * @cfg {String} language default en
22850  * 
22851  * @constructor
22852  * Create a new DateField
22853  * @param {Object} config The config object
22854  */
22855
22856 Roo.bootstrap.form.DateField = function(config){
22857     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22858      this.addEvents({
22859             /**
22860              * @event show
22861              * Fires when this field show.
22862              * @param {Roo.bootstrap.form.DateField} this
22863              * @param {Mixed} date The date value
22864              */
22865             show : true,
22866             /**
22867              * @event show
22868              * Fires when this field hide.
22869              * @param {Roo.bootstrap.form.DateField} this
22870              * @param {Mixed} date The date value
22871              */
22872             hide : true,
22873             /**
22874              * @event select
22875              * Fires when select a date.
22876              * @param {Roo.bootstrap.form.DateField} this
22877              * @param {Mixed} date The date value
22878              */
22879             select : true,
22880             /**
22881              * @event beforeselect
22882              * Fires when before select a date.
22883              * @param {Roo.bootstrap.form.DateField} this
22884              * @param {Mixed} date The date value
22885              */
22886             beforeselect : true
22887         });
22888 };
22889
22890 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22891     
22892     /**
22893      * @cfg {String} format
22894      * The default date format string which can be overriden for localization support.  The format must be
22895      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22896      */
22897     format : "m/d/y",
22898     /**
22899      * @cfg {String} altFormats
22900      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22901      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22902      */
22903     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22904     
22905     weekStart : 0,
22906     
22907     viewMode : '',
22908     
22909     minViewMode : '',
22910     
22911     todayHighlight : false,
22912     
22913     todayBtn: false,
22914     
22915     language: 'en',
22916     
22917     keyboardNavigation: true,
22918     
22919     calendarWeeks: false,
22920     
22921     startDate: -Infinity,
22922     
22923     endDate: Infinity,
22924     
22925     daysOfWeekDisabled: [],
22926     
22927     _events: [],
22928     
22929     singleMode : false,
22930     
22931     UTCDate: function()
22932     {
22933         return new Date(Date.UTC.apply(Date, arguments));
22934     },
22935     
22936     UTCToday: function()
22937     {
22938         var today = new Date();
22939         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22940     },
22941     
22942     getDate: function() {
22943             var d = this.getUTCDate();
22944             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22945     },
22946     
22947     getUTCDate: function() {
22948             return this.date;
22949     },
22950     
22951     setDate: function(d) {
22952             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22953     },
22954     
22955     setUTCDate: function(d) {
22956             this.date = d;
22957             this.setValue(this.formatDate(this.date));
22958     },
22959         
22960     onRender: function(ct, position)
22961     {
22962         
22963         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22964         
22965         this.language = this.language || 'en';
22966         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22967         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22968         
22969         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22970         this.format = this.format || 'm/d/y';
22971         this.isInline = false;
22972         this.isInput = true;
22973         this.component = this.el.select('.add-on', true).first() || false;
22974         this.component = (this.component && this.component.length === 0) ? false : this.component;
22975         this.hasInput = this.component && this.inputEl().length;
22976         
22977         if (typeof(this.minViewMode === 'string')) {
22978             switch (this.minViewMode) {
22979                 case 'months':
22980                     this.minViewMode = 1;
22981                     break;
22982                 case 'years':
22983                     this.minViewMode = 2;
22984                     break;
22985                 default:
22986                     this.minViewMode = 0;
22987                     break;
22988             }
22989         }
22990         
22991         if (typeof(this.viewMode === 'string')) {
22992             switch (this.viewMode) {
22993                 case 'months':
22994                     this.viewMode = 1;
22995                     break;
22996                 case 'years':
22997                     this.viewMode = 2;
22998                     break;
22999                 default:
23000                     this.viewMode = 0;
23001                     break;
23002             }
23003         }
23004                 
23005         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
23006         
23007 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
23008         
23009         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23010         
23011         this.picker().on('mousedown', this.onMousedown, this);
23012         this.picker().on('click', this.onClick, this);
23013         
23014         this.picker().addClass('datepicker-dropdown');
23015         
23016         this.startViewMode = this.viewMode;
23017         
23018         if(this.singleMode){
23019             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
23020                 v.setVisibilityMode(Roo.Element.DISPLAY);
23021                 v.hide();
23022             });
23023             
23024             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23025                 v.setStyle('width', '189px');
23026             });
23027         }
23028         
23029         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23030             if(!this.calendarWeeks){
23031                 v.remove();
23032                 return;
23033             }
23034             
23035             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23036             v.attr('colspan', function(i, val){
23037                 return parseInt(val) + 1;
23038             });
23039         });
23040                         
23041         
23042         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23043         
23044         this.setStartDate(this.startDate);
23045         this.setEndDate(this.endDate);
23046         
23047         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23048         
23049         this.fillDow();
23050         this.fillMonths();
23051         this.update();
23052         this.showMode();
23053         
23054         if(this.isInline) {
23055             this.showPopup();
23056         }
23057     },
23058     
23059     picker : function()
23060     {
23061         return this.pickerEl;
23062 //        return this.el.select('.datepicker', true).first();
23063     },
23064     
23065     fillDow: function()
23066     {
23067         var dowCnt = this.weekStart;
23068         
23069         var dow = {
23070             tag: 'tr',
23071             cn: [
23072                 
23073             ]
23074         };
23075         
23076         if(this.calendarWeeks){
23077             dow.cn.push({
23078                 tag: 'th',
23079                 cls: 'cw',
23080                 html: '&nbsp;'
23081             })
23082         }
23083         
23084         while (dowCnt < this.weekStart + 7) {
23085             dow.cn.push({
23086                 tag: 'th',
23087                 cls: 'dow',
23088                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23089             });
23090         }
23091         
23092         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23093     },
23094     
23095     fillMonths: function()
23096     {    
23097         var i = 0;
23098         var months = this.picker().select('>.datepicker-months td', true).first();
23099         
23100         months.dom.innerHTML = '';
23101         
23102         while (i < 12) {
23103             var month = {
23104                 tag: 'span',
23105                 cls: 'month',
23106                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23107             };
23108             
23109             months.createChild(month);
23110         }
23111         
23112     },
23113     
23114     update: function()
23115     {
23116         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;
23117         
23118         if (this.date < this.startDate) {
23119             this.viewDate = new Date(this.startDate);
23120         } else if (this.date > this.endDate) {
23121             this.viewDate = new Date(this.endDate);
23122         } else {
23123             this.viewDate = new Date(this.date);
23124         }
23125         
23126         this.fill();
23127     },
23128     
23129     fill: function() 
23130     {
23131         var d = new Date(this.viewDate),
23132                 year = d.getUTCFullYear(),
23133                 month = d.getUTCMonth(),
23134                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23135                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23136                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23137                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23138                 currentDate = this.date && this.date.valueOf(),
23139                 today = this.UTCToday();
23140         
23141         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23142         
23143 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23144         
23145 //        this.picker.select('>tfoot th.today').
23146 //                                              .text(dates[this.language].today)
23147 //                                              .toggle(this.todayBtn !== false);
23148     
23149         this.updateNavArrows();
23150         this.fillMonths();
23151                                                 
23152         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23153         
23154         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23155          
23156         prevMonth.setUTCDate(day);
23157         
23158         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23159         
23160         var nextMonth = new Date(prevMonth);
23161         
23162         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23163         
23164         nextMonth = nextMonth.valueOf();
23165         
23166         var fillMonths = false;
23167         
23168         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23169         
23170         while(prevMonth.valueOf() <= nextMonth) {
23171             var clsName = '';
23172             
23173             if (prevMonth.getUTCDay() === this.weekStart) {
23174                 if(fillMonths){
23175                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23176                 }
23177                     
23178                 fillMonths = {
23179                     tag: 'tr',
23180                     cn: []
23181                 };
23182                 
23183                 if(this.calendarWeeks){
23184                     // ISO 8601: First week contains first thursday.
23185                     // ISO also states week starts on Monday, but we can be more abstract here.
23186                     var
23187                     // Start of current week: based on weekstart/current date
23188                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23189                     // Thursday of this week
23190                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23191                     // First Thursday of year, year from thursday
23192                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23193                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23194                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23195                     
23196                     fillMonths.cn.push({
23197                         tag: 'td',
23198                         cls: 'cw',
23199                         html: calWeek
23200                     });
23201                 }
23202             }
23203             
23204             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23205                 clsName += ' old';
23206             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23207                 clsName += ' new';
23208             }
23209             if (this.todayHighlight &&
23210                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23211                 prevMonth.getUTCMonth() == today.getMonth() &&
23212                 prevMonth.getUTCDate() == today.getDate()) {
23213                 clsName += ' today';
23214             }
23215             
23216             if (currentDate && prevMonth.valueOf() === currentDate) {
23217                 clsName += ' active';
23218             }
23219             
23220             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23221                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23222                     clsName += ' disabled';
23223             }
23224             
23225             fillMonths.cn.push({
23226                 tag: 'td',
23227                 cls: 'day ' + clsName,
23228                 html: prevMonth.getDate()
23229             });
23230             
23231             prevMonth.setDate(prevMonth.getDate()+1);
23232         }
23233           
23234         var currentYear = this.date && this.date.getUTCFullYear();
23235         var currentMonth = this.date && this.date.getUTCMonth();
23236         
23237         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23238         
23239         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23240             v.removeClass('active');
23241             
23242             if(currentYear === year && k === currentMonth){
23243                 v.addClass('active');
23244             }
23245             
23246             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23247                 v.addClass('disabled');
23248             }
23249             
23250         });
23251         
23252         
23253         year = parseInt(year/10, 10) * 10;
23254         
23255         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23256         
23257         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23258         
23259         year -= 1;
23260         for (var i = -1; i < 11; i++) {
23261             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23262                 tag: 'span',
23263                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23264                 html: year
23265             });
23266             
23267             year += 1;
23268         }
23269     },
23270     
23271     showMode: function(dir) 
23272     {
23273         if (dir) {
23274             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23275         }
23276         
23277         Roo.each(this.picker().select('>div',true).elements, function(v){
23278             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23279             v.hide();
23280         });
23281         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23282     },
23283     
23284     place: function()
23285     {
23286         if(this.isInline) {
23287             return;
23288         }
23289         
23290         this.picker().removeClass(['bottom', 'top']);
23291         
23292         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23293             /*
23294              * place to the top of element!
23295              *
23296              */
23297             
23298             this.picker().addClass('top');
23299             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23300             
23301             return;
23302         }
23303         
23304         this.picker().addClass('bottom');
23305         
23306         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23307     },
23308     
23309     parseDate : function(value)
23310     {
23311         if(!value || value instanceof Date){
23312             return value;
23313         }
23314         var v = Date.parseDate(value, this.format);
23315         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23316             v = Date.parseDate(value, 'Y-m-d');
23317         }
23318         if(!v && this.altFormats){
23319             if(!this.altFormatsArray){
23320                 this.altFormatsArray = this.altFormats.split("|");
23321             }
23322             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23323                 v = Date.parseDate(value, this.altFormatsArray[i]);
23324             }
23325         }
23326         return v;
23327     },
23328     
23329     formatDate : function(date, fmt)
23330     {   
23331         return (!date || !(date instanceof Date)) ?
23332         date : date.dateFormat(fmt || this.format);
23333     },
23334     
23335     onFocus : function()
23336     {
23337         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23338         this.showPopup();
23339     },
23340     
23341     onBlur : function()
23342     {
23343         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23344         
23345         var d = this.inputEl().getValue();
23346         
23347         this.setValue(d);
23348                 
23349         this.hidePopup();
23350     },
23351     
23352     showPopup : function()
23353     {
23354         this.picker().show();
23355         this.update();
23356         this.place();
23357         
23358         this.fireEvent('showpopup', this, this.date);
23359     },
23360     
23361     hidePopup : function()
23362     {
23363         if(this.isInline) {
23364             return;
23365         }
23366         this.picker().hide();
23367         this.viewMode = this.startViewMode;
23368         this.showMode();
23369         
23370         this.fireEvent('hidepopup', this, this.date);
23371         
23372     },
23373     
23374     onMousedown: function(e)
23375     {
23376         e.stopPropagation();
23377         e.preventDefault();
23378     },
23379     
23380     keyup: function(e)
23381     {
23382         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23383         this.update();
23384     },
23385
23386     setValue: function(v)
23387     {
23388         if(this.fireEvent('beforeselect', this, v) !== false){
23389             var d = new Date(this.parseDate(v) ).clearTime();
23390         
23391             if(isNaN(d.getTime())){
23392                 this.date = this.viewDate = '';
23393                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23394                 return;
23395             }
23396
23397             v = this.formatDate(d);
23398
23399             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23400
23401             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23402
23403             this.update();
23404
23405             this.fireEvent('select', this, this.date);
23406         }
23407     },
23408     
23409     getValue: function()
23410     {
23411         return this.formatDate(this.date);
23412     },
23413     
23414     fireKey: function(e)
23415     {
23416         if (!this.picker().isVisible()){
23417             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23418                 this.showPopup();
23419             }
23420             return;
23421         }
23422         
23423         var dateChanged = false,
23424         dir, day, month,
23425         newDate, newViewDate;
23426         
23427         switch(e.keyCode){
23428             case 27: // escape
23429                 this.hidePopup();
23430                 e.preventDefault();
23431                 break;
23432             case 37: // left
23433             case 39: // right
23434                 if (!this.keyboardNavigation) {
23435                     break;
23436                 }
23437                 dir = e.keyCode == 37 ? -1 : 1;
23438                 
23439                 if (e.ctrlKey){
23440                     newDate = this.moveYear(this.date, dir);
23441                     newViewDate = this.moveYear(this.viewDate, dir);
23442                 } else if (e.shiftKey){
23443                     newDate = this.moveMonth(this.date, dir);
23444                     newViewDate = this.moveMonth(this.viewDate, dir);
23445                 } else {
23446                     newDate = new Date(this.date);
23447                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23448                     newViewDate = new Date(this.viewDate);
23449                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23450                 }
23451                 if (this.dateWithinRange(newDate)){
23452                     this.date = newDate;
23453                     this.viewDate = newViewDate;
23454                     this.setValue(this.formatDate(this.date));
23455 //                    this.update();
23456                     e.preventDefault();
23457                     dateChanged = true;
23458                 }
23459                 break;
23460             case 38: // up
23461             case 40: // down
23462                 if (!this.keyboardNavigation) {
23463                     break;
23464                 }
23465                 dir = e.keyCode == 38 ? -1 : 1;
23466                 if (e.ctrlKey){
23467                     newDate = this.moveYear(this.date, dir);
23468                     newViewDate = this.moveYear(this.viewDate, dir);
23469                 } else if (e.shiftKey){
23470                     newDate = this.moveMonth(this.date, dir);
23471                     newViewDate = this.moveMonth(this.viewDate, dir);
23472                 } else {
23473                     newDate = new Date(this.date);
23474                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23475                     newViewDate = new Date(this.viewDate);
23476                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23477                 }
23478                 if (this.dateWithinRange(newDate)){
23479                     this.date = newDate;
23480                     this.viewDate = newViewDate;
23481                     this.setValue(this.formatDate(this.date));
23482 //                    this.update();
23483                     e.preventDefault();
23484                     dateChanged = true;
23485                 }
23486                 break;
23487             case 13: // enter
23488                 this.setValue(this.formatDate(this.date));
23489                 this.hidePopup();
23490                 e.preventDefault();
23491                 break;
23492             case 9: // tab
23493                 this.setValue(this.formatDate(this.date));
23494                 this.hidePopup();
23495                 break;
23496             case 16: // shift
23497             case 17: // ctrl
23498             case 18: // alt
23499                 break;
23500             default :
23501                 this.hidePopup();
23502                 
23503         }
23504     },
23505     
23506     
23507     onClick: function(e) 
23508     {
23509         e.stopPropagation();
23510         e.preventDefault();
23511         
23512         var target = e.getTarget();
23513         
23514         if(target.nodeName.toLowerCase() === 'i'){
23515             target = Roo.get(target).dom.parentNode;
23516         }
23517         
23518         var nodeName = target.nodeName;
23519         var className = target.className;
23520         var html = target.innerHTML;
23521         //Roo.log(nodeName);
23522         
23523         switch(nodeName.toLowerCase()) {
23524             case 'th':
23525                 switch(className) {
23526                     case 'switch':
23527                         this.showMode(1);
23528                         break;
23529                     case 'prev':
23530                     case 'next':
23531                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23532                         switch(this.viewMode){
23533                                 case 0:
23534                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23535                                         break;
23536                                 case 1:
23537                                 case 2:
23538                                         this.viewDate = this.moveYear(this.viewDate, dir);
23539                                         break;
23540                         }
23541                         this.fill();
23542                         break;
23543                     case 'today':
23544                         var date = new Date();
23545                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23546 //                        this.fill()
23547                         this.setValue(this.formatDate(this.date));
23548                         
23549                         this.hidePopup();
23550                         break;
23551                 }
23552                 break;
23553             case 'span':
23554                 if (className.indexOf('disabled') < 0) {
23555                 if (!this.viewDate) {
23556                     this.viewDate = new Date();
23557                 }
23558                 this.viewDate.setUTCDate(1);
23559                     if (className.indexOf('month') > -1) {
23560                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23561                     } else {
23562                         var year = parseInt(html, 10) || 0;
23563                         this.viewDate.setUTCFullYear(year);
23564                         
23565                     }
23566                     
23567                     if(this.singleMode){
23568                         this.setValue(this.formatDate(this.viewDate));
23569                         this.hidePopup();
23570                         return;
23571                     }
23572                     
23573                     this.showMode(-1);
23574                     this.fill();
23575                 }
23576                 break;
23577                 
23578             case 'td':
23579                 //Roo.log(className);
23580                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23581                     var day = parseInt(html, 10) || 1;
23582                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23583                         month = (this.viewDate || new Date()).getUTCMonth();
23584
23585                     if (className.indexOf('old') > -1) {
23586                         if(month === 0 ){
23587                             month = 11;
23588                             year -= 1;
23589                         }else{
23590                             month -= 1;
23591                         }
23592                     } else if (className.indexOf('new') > -1) {
23593                         if (month == 11) {
23594                             month = 0;
23595                             year += 1;
23596                         } else {
23597                             month += 1;
23598                         }
23599                     }
23600                     //Roo.log([year,month,day]);
23601                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23602                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23603 //                    this.fill();
23604                     //Roo.log(this.formatDate(this.date));
23605                     this.setValue(this.formatDate(this.date));
23606                     this.hidePopup();
23607                 }
23608                 break;
23609         }
23610     },
23611     
23612     setStartDate: function(startDate)
23613     {
23614         this.startDate = startDate || -Infinity;
23615         if (this.startDate !== -Infinity) {
23616             this.startDate = this.parseDate(this.startDate);
23617         }
23618         this.update();
23619         this.updateNavArrows();
23620     },
23621
23622     setEndDate: function(endDate)
23623     {
23624         this.endDate = endDate || Infinity;
23625         if (this.endDate !== Infinity) {
23626             this.endDate = this.parseDate(this.endDate);
23627         }
23628         this.update();
23629         this.updateNavArrows();
23630     },
23631     
23632     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23633     {
23634         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23635         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23636             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23637         }
23638         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23639             return parseInt(d, 10);
23640         });
23641         this.update();
23642         this.updateNavArrows();
23643     },
23644     
23645     updateNavArrows: function() 
23646     {
23647         if(this.singleMode){
23648             return;
23649         }
23650         
23651         var d = new Date(this.viewDate),
23652         year = d.getUTCFullYear(),
23653         month = d.getUTCMonth();
23654         
23655         Roo.each(this.picker().select('.prev', true).elements, function(v){
23656             v.show();
23657             switch (this.viewMode) {
23658                 case 0:
23659
23660                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23661                         v.hide();
23662                     }
23663                     break;
23664                 case 1:
23665                 case 2:
23666                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23667                         v.hide();
23668                     }
23669                     break;
23670             }
23671         });
23672         
23673         Roo.each(this.picker().select('.next', true).elements, function(v){
23674             v.show();
23675             switch (this.viewMode) {
23676                 case 0:
23677
23678                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23679                         v.hide();
23680                     }
23681                     break;
23682                 case 1:
23683                 case 2:
23684                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23685                         v.hide();
23686                     }
23687                     break;
23688             }
23689         })
23690     },
23691     
23692     moveMonth: function(date, dir)
23693     {
23694         if (!dir) {
23695             return date;
23696         }
23697         var new_date = new Date(date.valueOf()),
23698         day = new_date.getUTCDate(),
23699         month = new_date.getUTCMonth(),
23700         mag = Math.abs(dir),
23701         new_month, test;
23702         dir = dir > 0 ? 1 : -1;
23703         if (mag == 1){
23704             test = dir == -1
23705             // If going back one month, make sure month is not current month
23706             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23707             ? function(){
23708                 return new_date.getUTCMonth() == month;
23709             }
23710             // If going forward one month, make sure month is as expected
23711             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23712             : function(){
23713                 return new_date.getUTCMonth() != new_month;
23714             };
23715             new_month = month + dir;
23716             new_date.setUTCMonth(new_month);
23717             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23718             if (new_month < 0 || new_month > 11) {
23719                 new_month = (new_month + 12) % 12;
23720             }
23721         } else {
23722             // For magnitudes >1, move one month at a time...
23723             for (var i=0; i<mag; i++) {
23724                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23725                 new_date = this.moveMonth(new_date, dir);
23726             }
23727             // ...then reset the day, keeping it in the new month
23728             new_month = new_date.getUTCMonth();
23729             new_date.setUTCDate(day);
23730             test = function(){
23731                 return new_month != new_date.getUTCMonth();
23732             };
23733         }
23734         // Common date-resetting loop -- if date is beyond end of month, make it
23735         // end of month
23736         while (test()){
23737             new_date.setUTCDate(--day);
23738             new_date.setUTCMonth(new_month);
23739         }
23740         return new_date;
23741     },
23742
23743     moveYear: function(date, dir)
23744     {
23745         return this.moveMonth(date, dir*12);
23746     },
23747
23748     dateWithinRange: function(date)
23749     {
23750         return date >= this.startDate && date <= this.endDate;
23751     },
23752
23753     
23754     remove: function() 
23755     {
23756         this.picker().remove();
23757     },
23758     
23759     validateValue : function(value)
23760     {
23761         if(this.getVisibilityEl().hasClass('hidden')){
23762             return true;
23763         }
23764         
23765         if(value.length < 1)  {
23766             if(this.allowBlank){
23767                 return true;
23768             }
23769             return false;
23770         }
23771         
23772         if(value.length < this.minLength){
23773             return false;
23774         }
23775         if(value.length > this.maxLength){
23776             return false;
23777         }
23778         if(this.vtype){
23779             var vt = Roo.form.VTypes;
23780             if(!vt[this.vtype](value, this)){
23781                 return false;
23782             }
23783         }
23784         if(typeof this.validator == "function"){
23785             var msg = this.validator(value);
23786             if(msg !== true){
23787                 return false;
23788             }
23789         }
23790         
23791         if(this.regex && !this.regex.test(value)){
23792             return false;
23793         }
23794         
23795         if(typeof(this.parseDate(value)) == 'undefined'){
23796             return false;
23797         }
23798         
23799         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23800             return false;
23801         }      
23802         
23803         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23804             return false;
23805         } 
23806         
23807         
23808         return true;
23809     },
23810     
23811     reset : function()
23812     {
23813         this.date = this.viewDate = '';
23814         
23815         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23816     }
23817    
23818 });
23819
23820 Roo.apply(Roo.bootstrap.form.DateField,  {
23821     
23822     head : {
23823         tag: 'thead',
23824         cn: [
23825         {
23826             tag: 'tr',
23827             cn: [
23828             {
23829                 tag: 'th',
23830                 cls: 'prev',
23831                 html: '<i class="fa fa-arrow-left"/>'
23832             },
23833             {
23834                 tag: 'th',
23835                 cls: 'switch',
23836                 colspan: '5'
23837             },
23838             {
23839                 tag: 'th',
23840                 cls: 'next',
23841                 html: '<i class="fa fa-arrow-right"/>'
23842             }
23843
23844             ]
23845         }
23846         ]
23847     },
23848     
23849     content : {
23850         tag: 'tbody',
23851         cn: [
23852         {
23853             tag: 'tr',
23854             cn: [
23855             {
23856                 tag: 'td',
23857                 colspan: '7'
23858             }
23859             ]
23860         }
23861         ]
23862     },
23863     
23864     footer : {
23865         tag: 'tfoot',
23866         cn: [
23867         {
23868             tag: 'tr',
23869             cn: [
23870             {
23871                 tag: 'th',
23872                 colspan: '7',
23873                 cls: 'today'
23874             }
23875                     
23876             ]
23877         }
23878         ]
23879     },
23880     
23881     dates:{
23882         en: {
23883             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23884             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23885             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23886             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23887             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23888             today: "Today"
23889         }
23890     },
23891     
23892     modes: [
23893     {
23894         clsName: 'days',
23895         navFnc: 'Month',
23896         navStep: 1
23897     },
23898     {
23899         clsName: 'months',
23900         navFnc: 'FullYear',
23901         navStep: 1
23902     },
23903     {
23904         clsName: 'years',
23905         navFnc: 'FullYear',
23906         navStep: 10
23907     }]
23908 });
23909
23910 Roo.apply(Roo.bootstrap.form.DateField,  {
23911   
23912     template : {
23913         tag: 'div',
23914         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23915         cn: [
23916         {
23917             tag: 'div',
23918             cls: 'datepicker-days',
23919             cn: [
23920             {
23921                 tag: 'table',
23922                 cls: 'table-condensed',
23923                 cn:[
23924                 Roo.bootstrap.form.DateField.head,
23925                 {
23926                     tag: 'tbody'
23927                 },
23928                 Roo.bootstrap.form.DateField.footer
23929                 ]
23930             }
23931             ]
23932         },
23933         {
23934             tag: 'div',
23935             cls: 'datepicker-months',
23936             cn: [
23937             {
23938                 tag: 'table',
23939                 cls: 'table-condensed',
23940                 cn:[
23941                 Roo.bootstrap.form.DateField.head,
23942                 Roo.bootstrap.form.DateField.content,
23943                 Roo.bootstrap.form.DateField.footer
23944                 ]
23945             }
23946             ]
23947         },
23948         {
23949             tag: 'div',
23950             cls: 'datepicker-years',
23951             cn: [
23952             {
23953                 tag: 'table',
23954                 cls: 'table-condensed',
23955                 cn:[
23956                 Roo.bootstrap.form.DateField.head,
23957                 Roo.bootstrap.form.DateField.content,
23958                 Roo.bootstrap.form.DateField.footer
23959                 ]
23960             }
23961             ]
23962         }
23963         ]
23964     }
23965 });
23966
23967  
23968
23969  /*
23970  * - LGPL
23971  *
23972  * TimeField
23973  * 
23974  */
23975
23976 /**
23977  * @class Roo.bootstrap.form.TimeField
23978  * @extends Roo.bootstrap.form.Input
23979  * Bootstrap DateField class
23980  * 
23981  * 
23982  * @constructor
23983  * Create a new TimeField
23984  * @param {Object} config The config object
23985  */
23986
23987 Roo.bootstrap.form.TimeField = function(config){
23988     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23989     this.addEvents({
23990             /**
23991              * @event show
23992              * Fires when this field show.
23993              * @param {Roo.bootstrap.form.DateField} thisthis
23994              * @param {Mixed} date The date value
23995              */
23996             show : true,
23997             /**
23998              * @event show
23999              * Fires when this field hide.
24000              * @param {Roo.bootstrap.form.DateField} this
24001              * @param {Mixed} date The date value
24002              */
24003             hide : true,
24004             /**
24005              * @event select
24006              * Fires when select a date.
24007              * @param {Roo.bootstrap.form.DateField} this
24008              * @param {Mixed} date The date value
24009              */
24010             select : true
24011         });
24012 };
24013
24014 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
24015     
24016     /**
24017      * @cfg {String} format
24018      * The default time format string which can be overriden for localization support.  The format must be
24019      * valid according to {@link Date#parseDate} (defaults to 'H:i').
24020      */
24021     format : "H:i",
24022
24023     getAutoCreate : function()
24024     {
24025         this.after = '<i class="fa far fa-clock"></i>';
24026         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24027         
24028          
24029     },
24030     onRender: function(ct, position)
24031     {
24032         
24033         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24034                 
24035         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24036         
24037         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24038         
24039         this.pop = this.picker().select('>.datepicker-time',true).first();
24040         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24041         
24042         this.picker().on('mousedown', this.onMousedown, this);
24043         this.picker().on('click', this.onClick, this);
24044         
24045         this.picker().addClass('datepicker-dropdown');
24046     
24047         this.fillTime();
24048         this.update();
24049             
24050         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24051         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24052         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24053         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24054         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24055         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24056
24057     },
24058     
24059     fireKey: function(e){
24060         if (!this.picker().isVisible()){
24061             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24062                 this.show();
24063             }
24064             return;
24065         }
24066
24067         e.preventDefault();
24068         
24069         switch(e.keyCode){
24070             case 27: // escape
24071                 this.hide();
24072                 break;
24073             case 37: // left
24074             case 39: // right
24075                 this.onTogglePeriod();
24076                 break;
24077             case 38: // up
24078                 this.onIncrementMinutes();
24079                 break;
24080             case 40: // down
24081                 this.onDecrementMinutes();
24082                 break;
24083             case 13: // enter
24084             case 9: // tab
24085                 this.setTime();
24086                 break;
24087         }
24088     },
24089     
24090     onClick: function(e) {
24091         e.stopPropagation();
24092         e.preventDefault();
24093     },
24094     
24095     picker : function()
24096     {
24097         return this.pickerEl;
24098     },
24099     
24100     fillTime: function()
24101     {    
24102         var time = this.pop.select('tbody', true).first();
24103         
24104         time.dom.innerHTML = '';
24105         
24106         time.createChild({
24107             tag: 'tr',
24108             cn: [
24109                 {
24110                     tag: 'td',
24111                     cn: [
24112                         {
24113                             tag: 'a',
24114                             href: '#',
24115                             cls: 'btn',
24116                             cn: [
24117                                 {
24118                                     tag: 'i',
24119                                     cls: 'hours-up fa fas fa-chevron-up'
24120                                 }
24121                             ]
24122                         } 
24123                     ]
24124                 },
24125                 {
24126                     tag: 'td',
24127                     cls: 'separator'
24128                 },
24129                 {
24130                     tag: 'td',
24131                     cn: [
24132                         {
24133                             tag: 'a',
24134                             href: '#',
24135                             cls: 'btn',
24136                             cn: [
24137                                 {
24138                                     tag: 'i',
24139                                     cls: 'minutes-up fa fas fa-chevron-up'
24140                                 }
24141                             ]
24142                         }
24143                     ]
24144                 },
24145                 {
24146                     tag: 'td',
24147                     cls: 'separator'
24148                 }
24149             ]
24150         });
24151         
24152         time.createChild({
24153             tag: 'tr',
24154             cn: [
24155                 {
24156                     tag: 'td',
24157                     cn: [
24158                         {
24159                             tag: 'span',
24160                             cls: 'timepicker-hour',
24161                             html: '00'
24162                         }  
24163                     ]
24164                 },
24165                 {
24166                     tag: 'td',
24167                     cls: 'separator',
24168                     html: ':'
24169                 },
24170                 {
24171                     tag: 'td',
24172                     cn: [
24173                         {
24174                             tag: 'span',
24175                             cls: 'timepicker-minute',
24176                             html: '00'
24177                         }  
24178                     ]
24179                 },
24180                 {
24181                     tag: 'td',
24182                     cls: 'separator'
24183                 },
24184                 {
24185                     tag: 'td',
24186                     cn: [
24187                         {
24188                             tag: 'button',
24189                             type: 'button',
24190                             cls: 'btn btn-primary period',
24191                             html: 'AM'
24192                             
24193                         }
24194                     ]
24195                 }
24196             ]
24197         });
24198         
24199         time.createChild({
24200             tag: 'tr',
24201             cn: [
24202                 {
24203                     tag: 'td',
24204                     cn: [
24205                         {
24206                             tag: 'a',
24207                             href: '#',
24208                             cls: 'btn',
24209                             cn: [
24210                                 {
24211                                     tag: 'span',
24212                                     cls: 'hours-down fa fas fa-chevron-down'
24213                                 }
24214                             ]
24215                         }
24216                     ]
24217                 },
24218                 {
24219                     tag: 'td',
24220                     cls: 'separator'
24221                 },
24222                 {
24223                     tag: 'td',
24224                     cn: [
24225                         {
24226                             tag: 'a',
24227                             href: '#',
24228                             cls: 'btn',
24229                             cn: [
24230                                 {
24231                                     tag: 'span',
24232                                     cls: 'minutes-down fa fas fa-chevron-down'
24233                                 }
24234                             ]
24235                         }
24236                     ]
24237                 },
24238                 {
24239                     tag: 'td',
24240                     cls: 'separator'
24241                 }
24242             ]
24243         });
24244         
24245     },
24246     
24247     update: function()
24248     {
24249         
24250         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24251         
24252         this.fill();
24253     },
24254     
24255     fill: function() 
24256     {
24257         var hours = this.time.getHours();
24258         var minutes = this.time.getMinutes();
24259         var period = 'AM';
24260         
24261         if(hours > 11){
24262             period = 'PM';
24263         }
24264         
24265         if(hours == 0){
24266             hours = 12;
24267         }
24268         
24269         
24270         if(hours > 12){
24271             hours = hours - 12;
24272         }
24273         
24274         if(hours < 10){
24275             hours = '0' + hours;
24276         }
24277         
24278         if(minutes < 10){
24279             minutes = '0' + minutes;
24280         }
24281         
24282         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24283         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24284         this.pop.select('button', true).first().dom.innerHTML = period;
24285         
24286     },
24287     
24288     place: function()
24289     {   
24290         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24291         
24292         var cls = ['bottom'];
24293         
24294         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24295             cls.pop();
24296             cls.push('top');
24297         }
24298         
24299         cls.push('right');
24300         
24301         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24302             cls.pop();
24303             cls.push('left');
24304         }
24305         //this.picker().setXY(20000,20000);
24306         this.picker().addClass(cls.join('-'));
24307         
24308         var _this = this;
24309         
24310         Roo.each(cls, function(c){
24311             if(c == 'bottom'){
24312                 (function() {
24313                  //  
24314                 }).defer(200);
24315                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24316                 //_this.picker().setTop(_this.inputEl().getHeight());
24317                 return;
24318             }
24319             if(c == 'top'){
24320                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24321                 
24322                 //_this.picker().setTop(0 - _this.picker().getHeight());
24323                 return;
24324             }
24325             /*
24326             if(c == 'left'){
24327                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24328                 return;
24329             }
24330             if(c == 'right'){
24331                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24332                 return;
24333             }
24334             */
24335         });
24336         
24337     },
24338   
24339     onFocus : function()
24340     {
24341         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24342         this.show();
24343     },
24344     
24345     onBlur : function()
24346     {
24347         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24348         this.hide();
24349     },
24350     
24351     show : function()
24352     {
24353         this.picker().show();
24354         this.pop.show();
24355         this.update();
24356         this.place();
24357         
24358         this.fireEvent('show', this, this.date);
24359     },
24360     
24361     hide : function()
24362     {
24363         this.picker().hide();
24364         this.pop.hide();
24365         
24366         this.fireEvent('hide', this, this.date);
24367     },
24368     
24369     setTime : function()
24370     {
24371         this.hide();
24372         this.setValue(this.time.format(this.format));
24373         
24374         this.fireEvent('select', this, this.date);
24375         
24376         
24377     },
24378     
24379     onMousedown: function(e){
24380         e.stopPropagation();
24381         e.preventDefault();
24382     },
24383     
24384     onIncrementHours: function()
24385     {
24386         Roo.log('onIncrementHours');
24387         this.time = this.time.add(Date.HOUR, 1);
24388         this.update();
24389         
24390     },
24391     
24392     onDecrementHours: function()
24393     {
24394         Roo.log('onDecrementHours');
24395         this.time = this.time.add(Date.HOUR, -1);
24396         this.update();
24397     },
24398     
24399     onIncrementMinutes: function()
24400     {
24401         Roo.log('onIncrementMinutes');
24402         this.time = this.time.add(Date.MINUTE, 1);
24403         this.update();
24404     },
24405     
24406     onDecrementMinutes: function()
24407     {
24408         Roo.log('onDecrementMinutes');
24409         this.time = this.time.add(Date.MINUTE, -1);
24410         this.update();
24411     },
24412     
24413     onTogglePeriod: function()
24414     {
24415         Roo.log('onTogglePeriod');
24416         this.time = this.time.add(Date.HOUR, 12);
24417         this.update();
24418     }
24419     
24420    
24421 });
24422  
24423
24424 Roo.apply(Roo.bootstrap.form.TimeField,  {
24425   
24426     template : {
24427         tag: 'div',
24428         cls: 'datepicker dropdown-menu',
24429         cn: [
24430             {
24431                 tag: 'div',
24432                 cls: 'datepicker-time',
24433                 cn: [
24434                 {
24435                     tag: 'table',
24436                     cls: 'table-condensed',
24437                     cn:[
24438                         {
24439                             tag: 'tbody',
24440                             cn: [
24441                                 {
24442                                     tag: 'tr',
24443                                     cn: [
24444                                     {
24445                                         tag: 'td',
24446                                         colspan: '7'
24447                                     }
24448                                     ]
24449                                 }
24450                             ]
24451                         },
24452                         {
24453                             tag: 'tfoot',
24454                             cn: [
24455                                 {
24456                                     tag: 'tr',
24457                                     cn: [
24458                                     {
24459                                         tag: 'th',
24460                                         colspan: '7',
24461                                         cls: '',
24462                                         cn: [
24463                                             {
24464                                                 tag: 'button',
24465                                                 cls: 'btn btn-info ok',
24466                                                 html: 'OK'
24467                                             }
24468                                         ]
24469                                     }
24470                     
24471                                     ]
24472                                 }
24473                             ]
24474                         }
24475                     ]
24476                 }
24477                 ]
24478             }
24479         ]
24480     }
24481 });
24482
24483  
24484
24485  /*
24486  * - LGPL
24487  *
24488  * MonthField
24489  * 
24490  */
24491
24492 /**
24493  * @class Roo.bootstrap.form.MonthField
24494  * @extends Roo.bootstrap.form.Input
24495  * Bootstrap MonthField class
24496  * 
24497  * @cfg {String} language default en
24498  * 
24499  * @constructor
24500  * Create a new MonthField
24501  * @param {Object} config The config object
24502  */
24503
24504 Roo.bootstrap.form.MonthField = function(config){
24505     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24506     
24507     this.addEvents({
24508         /**
24509          * @event show
24510          * Fires when this field show.
24511          * @param {Roo.bootstrap.form.MonthField} this
24512          * @param {Mixed} date The date value
24513          */
24514         show : true,
24515         /**
24516          * @event show
24517          * Fires when this field hide.
24518          * @param {Roo.bootstrap.form.MonthField} this
24519          * @param {Mixed} date The date value
24520          */
24521         hide : true,
24522         /**
24523          * @event select
24524          * Fires when select a date.
24525          * @param {Roo.bootstrap.form.MonthField} this
24526          * @param {String} oldvalue The old value
24527          * @param {String} newvalue The new value
24528          */
24529         select : true
24530     });
24531 };
24532
24533 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24534     
24535     onRender: function(ct, position)
24536     {
24537         
24538         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24539         
24540         this.language = this.language || 'en';
24541         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24542         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24543         
24544         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24545         this.isInline = false;
24546         this.isInput = true;
24547         this.component = this.el.select('.add-on', true).first() || false;
24548         this.component = (this.component && this.component.length === 0) ? false : this.component;
24549         this.hasInput = this.component && this.inputEL().length;
24550         
24551         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24552         
24553         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24554         
24555         this.picker().on('mousedown', this.onMousedown, this);
24556         this.picker().on('click', this.onClick, this);
24557         
24558         this.picker().addClass('datepicker-dropdown');
24559         
24560         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24561             v.setStyle('width', '189px');
24562         });
24563         
24564         this.fillMonths();
24565         
24566         this.update();
24567         
24568         if(this.isInline) {
24569             this.show();
24570         }
24571         
24572     },
24573     
24574     setValue: function(v, suppressEvent)
24575     {   
24576         var o = this.getValue();
24577         
24578         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24579         
24580         this.update();
24581
24582         if(suppressEvent !== true){
24583             this.fireEvent('select', this, o, v);
24584         }
24585         
24586     },
24587     
24588     getValue: function()
24589     {
24590         return this.value;
24591     },
24592     
24593     onClick: function(e) 
24594     {
24595         e.stopPropagation();
24596         e.preventDefault();
24597         
24598         var target = e.getTarget();
24599         
24600         if(target.nodeName.toLowerCase() === 'i'){
24601             target = Roo.get(target).dom.parentNode;
24602         }
24603         
24604         var nodeName = target.nodeName;
24605         var className = target.className;
24606         var html = target.innerHTML;
24607         
24608         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24609             return;
24610         }
24611         
24612         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24613         
24614         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24615         
24616         this.hide();
24617                         
24618     },
24619     
24620     picker : function()
24621     {
24622         return this.pickerEl;
24623     },
24624     
24625     fillMonths: function()
24626     {    
24627         var i = 0;
24628         var months = this.picker().select('>.datepicker-months td', true).first();
24629         
24630         months.dom.innerHTML = '';
24631         
24632         while (i < 12) {
24633             var month = {
24634                 tag: 'span',
24635                 cls: 'month',
24636                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24637             };
24638             
24639             months.createChild(month);
24640         }
24641         
24642     },
24643     
24644     update: function()
24645     {
24646         var _this = this;
24647         
24648         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24649             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24650         }
24651         
24652         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24653             e.removeClass('active');
24654             
24655             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24656                 e.addClass('active');
24657             }
24658         })
24659     },
24660     
24661     place: function()
24662     {
24663         if(this.isInline) {
24664             return;
24665         }
24666         
24667         this.picker().removeClass(['bottom', 'top']);
24668         
24669         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24670             /*
24671              * place to the top of element!
24672              *
24673              */
24674             
24675             this.picker().addClass('top');
24676             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24677             
24678             return;
24679         }
24680         
24681         this.picker().addClass('bottom');
24682         
24683         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24684     },
24685     
24686     onFocus : function()
24687     {
24688         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24689         this.show();
24690     },
24691     
24692     onBlur : function()
24693     {
24694         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24695         
24696         var d = this.inputEl().getValue();
24697         
24698         this.setValue(d);
24699                 
24700         this.hide();
24701     },
24702     
24703     show : function()
24704     {
24705         this.picker().show();
24706         this.picker().select('>.datepicker-months', true).first().show();
24707         this.update();
24708         this.place();
24709         
24710         this.fireEvent('show', this, this.date);
24711     },
24712     
24713     hide : function()
24714     {
24715         if(this.isInline) {
24716             return;
24717         }
24718         this.picker().hide();
24719         this.fireEvent('hide', this, this.date);
24720         
24721     },
24722     
24723     onMousedown: function(e)
24724     {
24725         e.stopPropagation();
24726         e.preventDefault();
24727     },
24728     
24729     keyup: function(e)
24730     {
24731         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24732         this.update();
24733     },
24734
24735     fireKey: function(e)
24736     {
24737         if (!this.picker().isVisible()){
24738             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24739                 this.show();
24740             }
24741             return;
24742         }
24743         
24744         var dir;
24745         
24746         switch(e.keyCode){
24747             case 27: // escape
24748                 this.hide();
24749                 e.preventDefault();
24750                 break;
24751             case 37: // left
24752             case 39: // right
24753                 dir = e.keyCode == 37 ? -1 : 1;
24754                 
24755                 this.vIndex = this.vIndex + dir;
24756                 
24757                 if(this.vIndex < 0){
24758                     this.vIndex = 0;
24759                 }
24760                 
24761                 if(this.vIndex > 11){
24762                     this.vIndex = 11;
24763                 }
24764                 
24765                 if(isNaN(this.vIndex)){
24766                     this.vIndex = 0;
24767                 }
24768                 
24769                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24770                 
24771                 break;
24772             case 38: // up
24773             case 40: // down
24774                 
24775                 dir = e.keyCode == 38 ? -1 : 1;
24776                 
24777                 this.vIndex = this.vIndex + dir * 4;
24778                 
24779                 if(this.vIndex < 0){
24780                     this.vIndex = 0;
24781                 }
24782                 
24783                 if(this.vIndex > 11){
24784                     this.vIndex = 11;
24785                 }
24786                 
24787                 if(isNaN(this.vIndex)){
24788                     this.vIndex = 0;
24789                 }
24790                 
24791                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24792                 break;
24793                 
24794             case 13: // enter
24795                 
24796                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24797                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24798                 }
24799                 
24800                 this.hide();
24801                 e.preventDefault();
24802                 break;
24803             case 9: // tab
24804                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24805                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24806                 }
24807                 this.hide();
24808                 break;
24809             case 16: // shift
24810             case 17: // ctrl
24811             case 18: // alt
24812                 break;
24813             default :
24814                 this.hide();
24815                 
24816         }
24817     },
24818     
24819     remove: function() 
24820     {
24821         this.picker().remove();
24822     }
24823    
24824 });
24825
24826 Roo.apply(Roo.bootstrap.form.MonthField,  {
24827     
24828     content : {
24829         tag: 'tbody',
24830         cn: [
24831         {
24832             tag: 'tr',
24833             cn: [
24834             {
24835                 tag: 'td',
24836                 colspan: '7'
24837             }
24838             ]
24839         }
24840         ]
24841     },
24842     
24843     dates:{
24844         en: {
24845             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24846             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24847         }
24848     }
24849 });
24850
24851 Roo.apply(Roo.bootstrap.form.MonthField,  {
24852   
24853     template : {
24854         tag: 'div',
24855         cls: 'datepicker dropdown-menu roo-dynamic',
24856         cn: [
24857             {
24858                 tag: 'div',
24859                 cls: 'datepicker-months',
24860                 cn: [
24861                 {
24862                     tag: 'table',
24863                     cls: 'table-condensed',
24864                     cn:[
24865                         Roo.bootstrap.form.DateField.content
24866                     ]
24867                 }
24868                 ]
24869             }
24870         ]
24871     }
24872 });
24873
24874  
24875
24876  
24877  /*
24878  * - LGPL
24879  *
24880  * CheckBox
24881  * 
24882  */
24883
24884 /**
24885  * @class Roo.bootstrap.form.CheckBox
24886  * @extends Roo.bootstrap.form.Input
24887  * Bootstrap CheckBox class
24888  * 
24889  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24890  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24891  * @cfg {String} boxLabel The text that appears beside the checkbox
24892  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24893  * @cfg {Boolean} checked initnal the element
24894  * @cfg {Boolean} inline inline the element (default false)
24895  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24896  * @cfg {String} tooltip label tooltip
24897  * 
24898  * @constructor
24899  * Create a new CheckBox
24900  * @param {Object} config The config object
24901  */
24902
24903 Roo.bootstrap.form.CheckBox = function(config){
24904     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24905    
24906     this.addEvents({
24907         /**
24908         * @event check
24909         * Fires when the element is checked or unchecked.
24910         * @param {Roo.bootstrap.form.CheckBox} this This input
24911         * @param {Boolean} checked The new checked value
24912         */
24913        check : true,
24914        /**
24915         * @event click
24916         * Fires when the element is click.
24917         * @param {Roo.bootstrap.form.CheckBox} this This input
24918         */
24919        click : true
24920     });
24921     
24922 };
24923
24924 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24925   
24926     inputType: 'checkbox',
24927     inputValue: 1,
24928     valueOff: 0,
24929     boxLabel: false,
24930     checked: false,
24931     weight : false,
24932     inline: false,
24933     tooltip : '',
24934     
24935     // checkbox success does not make any sense really.. 
24936     invalidClass : "",
24937     validClass : "",
24938     
24939     
24940     getAutoCreate : function()
24941     {
24942         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24943         
24944         var id = Roo.id();
24945         
24946         var cfg = {};
24947         
24948         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24949         
24950         if(this.inline){
24951             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24952         }
24953         
24954         var input =  {
24955             tag: 'input',
24956             id : id,
24957             type : this.inputType,
24958             value : this.inputValue,
24959             cls : 'roo-' + this.inputType, //'form-box',
24960             placeholder : this.placeholder || ''
24961             
24962         };
24963         
24964         if(this.inputType != 'radio'){
24965             var hidden =  {
24966                 tag: 'input',
24967                 type : 'hidden',
24968                 cls : 'roo-hidden-value',
24969                 value : this.checked ? this.inputValue : this.valueOff
24970             };
24971         }
24972         
24973             
24974         if (this.weight) { // Validity check?
24975             cfg.cls += " " + this.inputType + "-" + this.weight;
24976         }
24977         
24978         if (this.disabled) {
24979             input.disabled=true;
24980         }
24981         
24982         if(this.checked){
24983             input.checked = this.checked;
24984         }
24985         
24986         if (this.name) {
24987             
24988             input.name = this.name;
24989             
24990             if(this.inputType != 'radio'){
24991                 hidden.name = this.name;
24992                 input.name = '_hidden_' + this.name;
24993             }
24994         }
24995         
24996         if (this.size) {
24997             input.cls += ' input-' + this.size;
24998         }
24999         
25000         var settings=this;
25001         
25002         ['xs','sm','md','lg'].map(function(size){
25003             if (settings[size]) {
25004                 cfg.cls += ' col-' + size + '-' + settings[size];
25005             }
25006         });
25007         
25008         var inputblock = input;
25009          
25010         if (this.before || this.after) {
25011             
25012             inputblock = {
25013                 cls : 'input-group',
25014                 cn :  [] 
25015             };
25016             
25017             if (this.before) {
25018                 inputblock.cn.push({
25019                     tag :'span',
25020                     cls : 'input-group-addon',
25021                     html : this.before
25022                 });
25023             }
25024             
25025             inputblock.cn.push(input);
25026             
25027             if(this.inputType != 'radio'){
25028                 inputblock.cn.push(hidden);
25029             }
25030             
25031             if (this.after) {
25032                 inputblock.cn.push({
25033                     tag :'span',
25034                     cls : 'input-group-addon',
25035                     html : this.after
25036                 });
25037             }
25038             
25039         }
25040         var boxLabelCfg = false;
25041         
25042         if(this.boxLabel){
25043            
25044             boxLabelCfg = {
25045                 tag: 'label',
25046                 //'for': id, // box label is handled by onclick - so no for...
25047                 cls: 'box-label',
25048                 html: this.boxLabel
25049             };
25050             if(this.tooltip){
25051                 boxLabelCfg.tooltip = this.tooltip;
25052             }
25053              
25054         }
25055         
25056         
25057         if (align ==='left' && this.fieldLabel.length) {
25058 //                Roo.log("left and has label");
25059             cfg.cn = [
25060                 {
25061                     tag: 'label',
25062                     'for' :  id,
25063                     cls : 'control-label',
25064                     html : this.fieldLabel
25065                 },
25066                 {
25067                     cls : "", 
25068                     cn: [
25069                         inputblock
25070                     ]
25071                 }
25072             ];
25073             
25074             if (boxLabelCfg) {
25075                 cfg.cn[1].cn.push(boxLabelCfg);
25076             }
25077             
25078             if(this.labelWidth > 12){
25079                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25080             }
25081             
25082             if(this.labelWidth < 13 && this.labelmd == 0){
25083                 this.labelmd = this.labelWidth;
25084             }
25085             
25086             if(this.labellg > 0){
25087                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25088                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25089             }
25090             
25091             if(this.labelmd > 0){
25092                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25093                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25094             }
25095             
25096             if(this.labelsm > 0){
25097                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25098                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25099             }
25100             
25101             if(this.labelxs > 0){
25102                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25103                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25104             }
25105             
25106         } else if ( this.fieldLabel.length) {
25107 //                Roo.log(" label");
25108                 cfg.cn = [
25109                    
25110                     {
25111                         tag: this.boxLabel ? 'span' : 'label',
25112                         'for': id,
25113                         cls: 'control-label box-input-label',
25114                         //cls : 'input-group-addon',
25115                         html : this.fieldLabel
25116                     },
25117                     
25118                     inputblock
25119                     
25120                 ];
25121                 if (boxLabelCfg) {
25122                     cfg.cn.push(boxLabelCfg);
25123                 }
25124
25125         } else {
25126             
25127 //                Roo.log(" no label && no align");
25128                 cfg.cn = [  inputblock ] ;
25129                 if (boxLabelCfg) {
25130                     cfg.cn.push(boxLabelCfg);
25131                 }
25132
25133                 
25134         }
25135         
25136        
25137         
25138         if(this.inputType != 'radio'){
25139             cfg.cn.push(hidden);
25140         }
25141         
25142         return cfg;
25143         
25144     },
25145     
25146     /**
25147      * return the real input element.
25148      */
25149     inputEl: function ()
25150     {
25151         return this.el.select('input.roo-' + this.inputType,true).first();
25152     },
25153     hiddenEl: function ()
25154     {
25155         return this.el.select('input.roo-hidden-value',true).first();
25156     },
25157     
25158     labelEl: function()
25159     {
25160         return this.el.select('label.control-label',true).first();
25161     },
25162     /* depricated... */
25163     
25164     label: function()
25165     {
25166         return this.labelEl();
25167     },
25168     
25169     boxLabelEl: function()
25170     {
25171         return this.el.select('label.box-label',true).first();
25172     },
25173     
25174     initEvents : function()
25175     {
25176 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25177         
25178         this.inputEl().on('click', this.onClick,  this);
25179         
25180         if (this.boxLabel) { 
25181             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25182         }
25183         
25184         this.startValue = this.getValue();
25185         
25186         if(this.groupId){
25187             Roo.bootstrap.form.CheckBox.register(this);
25188         }
25189     },
25190     
25191     onClick : function(e)
25192     {   
25193         if(this.fireEvent('click', this, e) !== false){
25194             this.setChecked(!this.checked);
25195         }
25196         
25197     },
25198     
25199     setChecked : function(state,suppressEvent)
25200     {
25201         this.startValue = this.getValue();
25202
25203         if(this.inputType == 'radio'){
25204             
25205             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25206                 e.dom.checked = false;
25207             });
25208             
25209             this.inputEl().dom.checked = true;
25210             
25211             this.inputEl().dom.value = this.inputValue;
25212             
25213             if(suppressEvent !== true){
25214                 this.fireEvent('check', this, true);
25215             }
25216             
25217             this.validate();
25218             
25219             return;
25220         }
25221         
25222         this.checked = state;
25223         
25224         this.inputEl().dom.checked = state;
25225         
25226         
25227         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25228         
25229         if(suppressEvent !== true){
25230             this.fireEvent('check', this, state);
25231         }
25232         
25233         this.validate();
25234     },
25235     
25236     getValue : function()
25237     {
25238         if(this.inputType == 'radio'){
25239             return this.getGroupValue();
25240         }
25241         
25242         return this.hiddenEl().dom.value;
25243         
25244     },
25245     
25246     getGroupValue : function()
25247     {
25248         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25249             return '';
25250         }
25251         
25252         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25253     },
25254     
25255     setValue : function(v,suppressEvent)
25256     {
25257         if(this.inputType == 'radio'){
25258             this.setGroupValue(v, suppressEvent);
25259             return;
25260         }
25261         
25262         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25263         
25264         this.validate();
25265     },
25266     
25267     setGroupValue : function(v, suppressEvent)
25268     {
25269         this.startValue = this.getValue();
25270         
25271         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25272             e.dom.checked = false;
25273             
25274             if(e.dom.value == v){
25275                 e.dom.checked = true;
25276             }
25277         });
25278         
25279         if(suppressEvent !== true){
25280             this.fireEvent('check', this, true);
25281         }
25282
25283         this.validate();
25284         
25285         return;
25286     },
25287     
25288     validate : function()
25289     {
25290         if(this.getVisibilityEl().hasClass('hidden')){
25291             return true;
25292         }
25293         
25294         if(
25295                 this.disabled || 
25296                 (this.inputType == 'radio' && this.validateRadio()) ||
25297                 (this.inputType == 'checkbox' && this.validateCheckbox())
25298         ){
25299             this.markValid();
25300             return true;
25301         }
25302         
25303         this.markInvalid();
25304         return false;
25305     },
25306     
25307     validateRadio : function()
25308     {
25309         if(this.getVisibilityEl().hasClass('hidden')){
25310             return true;
25311         }
25312         
25313         if(this.allowBlank){
25314             return true;
25315         }
25316         
25317         var valid = false;
25318         
25319         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25320             if(!e.dom.checked){
25321                 return;
25322             }
25323             
25324             valid = true;
25325             
25326             return false;
25327         });
25328         
25329         return valid;
25330     },
25331     
25332     validateCheckbox : function()
25333     {
25334         if(!this.groupId){
25335             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25336             //return (this.getValue() == this.inputValue) ? true : false;
25337         }
25338         
25339         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25340         
25341         if(!group){
25342             return false;
25343         }
25344         
25345         var r = false;
25346         
25347         for(var i in group){
25348             if(group[i].el.isVisible(true)){
25349                 r = false;
25350                 break;
25351             }
25352             
25353             r = true;
25354         }
25355         
25356         for(var i in group){
25357             if(r){
25358                 break;
25359             }
25360             
25361             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25362         }
25363         
25364         return r;
25365     },
25366     
25367     /**
25368      * Mark this field as valid
25369      */
25370     markValid : function()
25371     {
25372         var _this = this;
25373         
25374         this.fireEvent('valid', this);
25375         
25376         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25377         
25378         if(this.groupId){
25379             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25380         }
25381         
25382         if(label){
25383             label.markValid();
25384         }
25385
25386         if(this.inputType == 'radio'){
25387             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25388                 var fg = e.findParent('.form-group', false, true);
25389                 if (Roo.bootstrap.version == 3) {
25390                     fg.removeClass([_this.invalidClass, _this.validClass]);
25391                     fg.addClass(_this.validClass);
25392                 } else {
25393                     fg.removeClass(['is-valid', 'is-invalid']);
25394                     fg.addClass('is-valid');
25395                 }
25396             });
25397             
25398             return;
25399         }
25400
25401         if(!this.groupId){
25402             var fg = this.el.findParent('.form-group', false, true);
25403             if (Roo.bootstrap.version == 3) {
25404                 fg.removeClass([this.invalidClass, this.validClass]);
25405                 fg.addClass(this.validClass);
25406             } else {
25407                 fg.removeClass(['is-valid', 'is-invalid']);
25408                 fg.addClass('is-valid');
25409             }
25410             return;
25411         }
25412         
25413         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25414         
25415         if(!group){
25416             return;
25417         }
25418         
25419         for(var i in group){
25420             var fg = group[i].el.findParent('.form-group', false, true);
25421             if (Roo.bootstrap.version == 3) {
25422                 fg.removeClass([this.invalidClass, this.validClass]);
25423                 fg.addClass(this.validClass);
25424             } else {
25425                 fg.removeClass(['is-valid', 'is-invalid']);
25426                 fg.addClass('is-valid');
25427             }
25428         }
25429     },
25430     
25431      /**
25432      * Mark this field as invalid
25433      * @param {String} msg The validation message
25434      */
25435     markInvalid : function(msg)
25436     {
25437         if(this.allowBlank){
25438             return;
25439         }
25440         
25441         var _this = this;
25442         
25443         this.fireEvent('invalid', this, msg);
25444         
25445         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25446         
25447         if(this.groupId){
25448             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25449         }
25450         
25451         if(label){
25452             label.markInvalid();
25453         }
25454             
25455         if(this.inputType == 'radio'){
25456             
25457             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25458                 var fg = e.findParent('.form-group', false, true);
25459                 if (Roo.bootstrap.version == 3) {
25460                     fg.removeClass([_this.invalidClass, _this.validClass]);
25461                     fg.addClass(_this.invalidClass);
25462                 } else {
25463                     fg.removeClass(['is-invalid', 'is-valid']);
25464                     fg.addClass('is-invalid');
25465                 }
25466             });
25467             
25468             return;
25469         }
25470         
25471         if(!this.groupId){
25472             var fg = this.el.findParent('.form-group', false, true);
25473             if (Roo.bootstrap.version == 3) {
25474                 fg.removeClass([_this.invalidClass, _this.validClass]);
25475                 fg.addClass(_this.invalidClass);
25476             } else {
25477                 fg.removeClass(['is-invalid', 'is-valid']);
25478                 fg.addClass('is-invalid');
25479             }
25480             return;
25481         }
25482         
25483         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25484         
25485         if(!group){
25486             return;
25487         }
25488         
25489         for(var i in group){
25490             var fg = group[i].el.findParent('.form-group', false, true);
25491             if (Roo.bootstrap.version == 3) {
25492                 fg.removeClass([_this.invalidClass, _this.validClass]);
25493                 fg.addClass(_this.invalidClass);
25494             } else {
25495                 fg.removeClass(['is-invalid', 'is-valid']);
25496                 fg.addClass('is-invalid');
25497             }
25498         }
25499         
25500     },
25501     
25502     clearInvalid : function()
25503     {
25504         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25505         
25506         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25507         
25508         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25509         
25510         if (label && label.iconEl) {
25511             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25512             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25513         }
25514     },
25515     
25516     disable : function()
25517     {
25518         if(this.inputType != 'radio'){
25519             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25520             return;
25521         }
25522         
25523         var _this = this;
25524         
25525         if(this.rendered){
25526             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25527                 _this.getActionEl().addClass(this.disabledClass);
25528                 e.dom.disabled = true;
25529             });
25530         }
25531         
25532         this.disabled = true;
25533         this.fireEvent("disable", this);
25534         return this;
25535     },
25536
25537     enable : function()
25538     {
25539         if(this.inputType != 'radio'){
25540             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25541             return;
25542         }
25543         
25544         var _this = this;
25545         
25546         if(this.rendered){
25547             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25548                 _this.getActionEl().removeClass(this.disabledClass);
25549                 e.dom.disabled = false;
25550             });
25551         }
25552         
25553         this.disabled = false;
25554         this.fireEvent("enable", this);
25555         return this;
25556     },
25557     
25558     setBoxLabel : function(v)
25559     {
25560         this.boxLabel = v;
25561         
25562         if(this.rendered){
25563             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25564         }
25565     }
25566
25567 });
25568
25569 Roo.apply(Roo.bootstrap.form.CheckBox, {
25570     
25571     groups: {},
25572     
25573      /**
25574     * register a CheckBox Group
25575     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25576     */
25577     register : function(checkbox)
25578     {
25579         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25580             this.groups[checkbox.groupId] = {};
25581         }
25582         
25583         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25584             return;
25585         }
25586         
25587         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25588         
25589     },
25590     /**
25591     * fetch a CheckBox Group based on the group ID
25592     * @param {string} the group ID
25593     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25594     */
25595     get: function(groupId) {
25596         if (typeof(this.groups[groupId]) == 'undefined') {
25597             return false;
25598         }
25599         
25600         return this.groups[groupId] ;
25601     }
25602     
25603     
25604 });
25605 /*
25606  * - LGPL
25607  *
25608  * RadioItem
25609  * 
25610  */
25611
25612 /**
25613  * @class Roo.bootstrap.form.Radio
25614  * @extends Roo.bootstrap.Component
25615  * Bootstrap Radio class
25616  * @cfg {String} boxLabel - the label associated
25617  * @cfg {String} value - the value of radio
25618  * 
25619  * @constructor
25620  * Create a new Radio
25621  * @param {Object} config The config object
25622  */
25623 Roo.bootstrap.form.Radio = function(config){
25624     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25625     
25626 };
25627
25628 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25629     
25630     boxLabel : '',
25631     
25632     value : '',
25633     
25634     getAutoCreate : function()
25635     {
25636         var cfg = {
25637             tag : 'div',
25638             cls : 'form-group radio',
25639             cn : [
25640                 {
25641                     tag : 'label',
25642                     cls : 'box-label',
25643                     html : this.boxLabel
25644                 }
25645             ]
25646         };
25647         
25648         return cfg;
25649     },
25650     
25651     initEvents : function() 
25652     {
25653         this.parent().register(this);
25654         
25655         this.el.on('click', this.onClick, this);
25656         
25657     },
25658     
25659     onClick : function(e)
25660     {
25661         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25662             this.setChecked(true);
25663         }
25664     },
25665     
25666     setChecked : function(state, suppressEvent)
25667     {
25668         this.parent().setValue(this.value, suppressEvent);
25669         
25670     },
25671     
25672     setBoxLabel : function(v)
25673     {
25674         this.boxLabel = v;
25675         
25676         if(this.rendered){
25677             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25678         }
25679     }
25680     
25681 });
25682  
25683
25684  /*
25685  * - LGPL
25686  *
25687  * Input
25688  * 
25689  */
25690
25691 /**
25692  * @class Roo.bootstrap.form.SecurePass
25693  * @extends Roo.bootstrap.form.Input
25694  * Bootstrap SecurePass class
25695  *
25696  * 
25697  * @constructor
25698  * Create a new SecurePass
25699  * @param {Object} config The config object
25700  */
25701  
25702 Roo.bootstrap.form.SecurePass = function (config) {
25703     // these go here, so the translation tool can replace them..
25704     this.errors = {
25705         PwdEmpty: "Please type a password, and then retype it to confirm.",
25706         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25707         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25708         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25709         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25710         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25711         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25712         TooWeak: "Your password is Too Weak."
25713     },
25714     this.meterLabel = "Password strength:";
25715     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25716     this.meterClass = [
25717         "roo-password-meter-tooweak", 
25718         "roo-password-meter-weak", 
25719         "roo-password-meter-medium", 
25720         "roo-password-meter-strong", 
25721         "roo-password-meter-grey"
25722     ];
25723     
25724     this.errors = {};
25725     
25726     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25727 }
25728
25729 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25730     /**
25731      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25732      * {
25733      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25734      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25735      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25736      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25737      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25738      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25739      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25740      * })
25741      */
25742     // private
25743     
25744     meterWidth: 300,
25745     errorMsg :'',    
25746     errors: false,
25747     imageRoot: '/',
25748     /**
25749      * @cfg {String/Object} Label for the strength meter (defaults to
25750      * 'Password strength:')
25751      */
25752     // private
25753     meterLabel: '',
25754     /**
25755      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25756      * ['Weak', 'Medium', 'Strong'])
25757      */
25758     // private    
25759     pwdStrengths: false,    
25760     // private
25761     strength: 0,
25762     // private
25763     _lastPwd: null,
25764     // private
25765     kCapitalLetter: 0,
25766     kSmallLetter: 1,
25767     kDigit: 2,
25768     kPunctuation: 3,
25769     
25770     insecure: false,
25771     // private
25772     initEvents: function ()
25773     {
25774         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25775
25776         if (this.el.is('input[type=password]') && Roo.isSafari) {
25777             this.el.on('keydown', this.SafariOnKeyDown, this);
25778         }
25779
25780         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25781     },
25782     // private
25783     onRender: function (ct, position)
25784     {
25785         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25786         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25787         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25788
25789         this.trigger.createChild({
25790                    cn: [
25791                     {
25792                     //id: 'PwdMeter',
25793                     tag: 'div',
25794                     cls: 'roo-password-meter-grey col-xs-12',
25795                     style: {
25796                         //width: 0,
25797                         //width: this.meterWidth + 'px'                                                
25798                         }
25799                     },
25800                     {                            
25801                          cls: 'roo-password-meter-text'                          
25802                     }
25803                 ]            
25804         });
25805
25806          
25807         if (this.hideTrigger) {
25808             this.trigger.setDisplayed(false);
25809         }
25810         this.setSize(this.width || '', this.height || '');
25811     },
25812     // private
25813     onDestroy: function ()
25814     {
25815         if (this.trigger) {
25816             this.trigger.removeAllListeners();
25817             this.trigger.remove();
25818         }
25819         if (this.wrap) {
25820             this.wrap.remove();
25821         }
25822         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25823     },
25824     // private
25825     checkStrength: function ()
25826     {
25827         var pwd = this.inputEl().getValue();
25828         if (pwd == this._lastPwd) {
25829             return;
25830         }
25831
25832         var strength;
25833         if (this.ClientSideStrongPassword(pwd)) {
25834             strength = 3;
25835         } else if (this.ClientSideMediumPassword(pwd)) {
25836             strength = 2;
25837         } else if (this.ClientSideWeakPassword(pwd)) {
25838             strength = 1;
25839         } else {
25840             strength = 0;
25841         }
25842         
25843         Roo.log('strength1: ' + strength);
25844         
25845         //var pm = this.trigger.child('div/div/div').dom;
25846         var pm = this.trigger.child('div/div');
25847         pm.removeClass(this.meterClass);
25848         pm.addClass(this.meterClass[strength]);
25849                 
25850         
25851         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25852                 
25853         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25854         
25855         this._lastPwd = pwd;
25856     },
25857     reset: function ()
25858     {
25859         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25860         
25861         this._lastPwd = '';
25862         
25863         var pm = this.trigger.child('div/div');
25864         pm.removeClass(this.meterClass);
25865         pm.addClass('roo-password-meter-grey');        
25866         
25867         
25868         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25869         
25870         pt.innerHTML = '';
25871         this.inputEl().dom.type='password';
25872     },
25873     // private
25874     validateValue: function (value)
25875     {
25876         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25877             return false;
25878         }
25879         if (value.length == 0) {
25880             if (this.allowBlank) {
25881                 this.clearInvalid();
25882                 return true;
25883             }
25884
25885             this.markInvalid(this.errors.PwdEmpty);
25886             this.errorMsg = this.errors.PwdEmpty;
25887             return false;
25888         }
25889         
25890         if(this.insecure){
25891             return true;
25892         }
25893         
25894         if (!value.match(/[\x21-\x7e]+/)) {
25895             this.markInvalid(this.errors.PwdBadChar);
25896             this.errorMsg = this.errors.PwdBadChar;
25897             return false;
25898         }
25899         if (value.length < 6) {
25900             this.markInvalid(this.errors.PwdShort);
25901             this.errorMsg = this.errors.PwdShort;
25902             return false;
25903         }
25904         if (value.length > 16) {
25905             this.markInvalid(this.errors.PwdLong);
25906             this.errorMsg = this.errors.PwdLong;
25907             return false;
25908         }
25909         var strength;
25910         if (this.ClientSideStrongPassword(value)) {
25911             strength = 3;
25912         } else if (this.ClientSideMediumPassword(value)) {
25913             strength = 2;
25914         } else if (this.ClientSideWeakPassword(value)) {
25915             strength = 1;
25916         } else {
25917             strength = 0;
25918         }
25919
25920         
25921         if (strength < 2) {
25922             //this.markInvalid(this.errors.TooWeak);
25923             this.errorMsg = this.errors.TooWeak;
25924             //return false;
25925         }
25926         
25927         
25928         console.log('strength2: ' + strength);
25929         
25930         //var pm = this.trigger.child('div/div/div').dom;
25931         
25932         var pm = this.trigger.child('div/div');
25933         pm.removeClass(this.meterClass);
25934         pm.addClass(this.meterClass[strength]);
25935                 
25936         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25937                 
25938         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25939         
25940         this.errorMsg = ''; 
25941         return true;
25942     },
25943     // private
25944     CharacterSetChecks: function (type)
25945     {
25946         this.type = type;
25947         this.fResult = false;
25948     },
25949     // private
25950     isctype: function (character, type)
25951     {
25952         switch (type) {  
25953             case this.kCapitalLetter:
25954                 if (character >= 'A' && character <= 'Z') {
25955                     return true;
25956                 }
25957                 break;
25958             
25959             case this.kSmallLetter:
25960                 if (character >= 'a' && character <= 'z') {
25961                     return true;
25962                 }
25963                 break;
25964             
25965             case this.kDigit:
25966                 if (character >= '0' && character <= '9') {
25967                     return true;
25968                 }
25969                 break;
25970             
25971             case this.kPunctuation:
25972                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25973                     return true;
25974                 }
25975                 break;
25976             
25977             default:
25978                 return false;
25979         }
25980
25981     },
25982     // private
25983     IsLongEnough: function (pwd, size)
25984     {
25985         return !(pwd == null || isNaN(size) || pwd.length < size);
25986     },
25987     // private
25988     SpansEnoughCharacterSets: function (word, nb)
25989     {
25990         if (!this.IsLongEnough(word, nb))
25991         {
25992             return false;
25993         }
25994
25995         var characterSetChecks = new Array(
25996             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25997             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25998         );
25999         
26000         for (var index = 0; index < word.length; ++index) {
26001             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26002                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
26003                     characterSetChecks[nCharSet].fResult = true;
26004                     break;
26005                 }
26006             }
26007         }
26008
26009         var nCharSets = 0;
26010         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26011             if (characterSetChecks[nCharSet].fResult) {
26012                 ++nCharSets;
26013             }
26014         }
26015
26016         if (nCharSets < nb) {
26017             return false;
26018         }
26019         return true;
26020     },
26021     // private
26022     ClientSideStrongPassword: function (pwd)
26023     {
26024         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26025     },
26026     // private
26027     ClientSideMediumPassword: function (pwd)
26028     {
26029         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26030     },
26031     // private
26032     ClientSideWeakPassword: function (pwd)
26033     {
26034         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26035     }
26036           
26037 });
26038 Roo.htmleditor = {};
26039  
26040 /**
26041  * @class Roo.htmleditor.Filter
26042  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26043  * @cfg {DomElement} node The node to iterate and filter
26044  * @cfg {boolean|String|Array} tag Tags to replace 
26045  * @constructor
26046  * Create a new Filter.
26047  * @param {Object} config Configuration options
26048  */
26049
26050
26051
26052 Roo.htmleditor.Filter = function(cfg) {
26053     Roo.apply(this.cfg);
26054     // this does not actually call walk as it's really just a abstract class
26055 }
26056
26057
26058 Roo.htmleditor.Filter.prototype = {
26059     
26060     node: false,
26061     
26062     tag: false,
26063
26064     // overrride to do replace comments.
26065     replaceComment : false,
26066     
26067     // overrride to do replace or do stuff with tags..
26068     replaceTag : false,
26069     
26070     walk : function(dom)
26071     {
26072         Roo.each( Array.from(dom.childNodes), function( e ) {
26073             switch(true) {
26074                 
26075                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26076                     this.replaceComment(e);
26077                     return;
26078                 
26079                 case e.nodeType != 1: //not a node.
26080                     return;
26081                 
26082                 case this.tag === true: // everything
26083                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26084                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26085                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26086                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26087                     if (this.replaceTag && false === this.replaceTag(e)) {
26088                         return;
26089                     }
26090                     if (e.hasChildNodes()) {
26091                         this.walk(e);
26092                     }
26093                     return;
26094                 
26095                 default:    // tags .. that do not match.
26096                     if (e.hasChildNodes()) {
26097                         this.walk(e);
26098                     }
26099             }
26100             
26101         }, this);
26102         
26103     },
26104     
26105     
26106     removeNodeKeepChildren : function( node)
26107     {
26108     
26109         ar = Array.from(node.childNodes);
26110         for (var i = 0; i < ar.length; i++) {
26111          
26112             node.removeChild(ar[i]);
26113             // what if we need to walk these???
26114             node.parentNode.insertBefore(ar[i], node);
26115            
26116         }
26117         node.parentNode.removeChild(node);
26118     }
26119 }; 
26120
26121 /**
26122  * @class Roo.htmleditor.FilterAttributes
26123  * clean attributes and  styles including http:// etc.. in attribute
26124  * @constructor
26125 * Run a new Attribute Filter
26126 * @param {Object} config Configuration options
26127  */
26128 Roo.htmleditor.FilterAttributes = function(cfg)
26129 {
26130     Roo.apply(this, cfg);
26131     this.attrib_black = this.attrib_black || [];
26132     this.attrib_white = this.attrib_white || [];
26133
26134     this.attrib_clean = this.attrib_clean || [];
26135     this.style_white = this.style_white || [];
26136     this.style_black = this.style_black || [];
26137     this.walk(cfg.node);
26138 }
26139
26140 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26141 {
26142     tag: true, // all tags
26143     
26144     attrib_black : false, // array
26145     attrib_clean : false,
26146     attrib_white : false,
26147
26148     style_white : false,
26149     style_black : false,
26150      
26151      
26152     replaceTag : function(node)
26153     {
26154         if (!node.attributes || !node.attributes.length) {
26155             return true;
26156         }
26157         
26158         for (var i = node.attributes.length-1; i > -1 ; i--) {
26159             var a = node.attributes[i];
26160             //console.log(a);
26161             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26162                 node.removeAttribute(a.name);
26163                 continue;
26164             }
26165             
26166             
26167             
26168             if (a.name.toLowerCase().substr(0,2)=='on')  {
26169                 node.removeAttribute(a.name);
26170                 continue;
26171             }
26172             
26173             
26174             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26175                 node.removeAttribute(a.name);
26176                 continue;
26177             }
26178             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26179                 this.cleanAttr(node,a.name,a.value); // fixme..
26180                 continue;
26181             }
26182             if (a.name == 'style') {
26183                 this.cleanStyle(node,a.name,a.value);
26184                 continue;
26185             }
26186             /// clean up MS crap..
26187             // tecnically this should be a list of valid class'es..
26188             
26189             
26190             if (a.name == 'class') {
26191                 if (a.value.match(/^Mso/)) {
26192                     node.removeAttribute('class');
26193                 }
26194                 
26195                 if (a.value.match(/^body$/)) {
26196                     node.removeAttribute('class');
26197                 }
26198                 continue;
26199             }
26200             
26201             
26202             // style cleanup!?
26203             // class cleanup?
26204             
26205         }
26206         return true; // clean children
26207     },
26208         
26209     cleanAttr: function(node, n,v)
26210     {
26211         
26212         if (v.match(/^\./) || v.match(/^\//)) {
26213             return;
26214         }
26215         if (v.match(/^(http|https):\/\//)
26216             || v.match(/^mailto:/) 
26217             || v.match(/^ftp:/)
26218             || v.match(/^data:/)
26219             ) {
26220             return;
26221         }
26222         if (v.match(/^#/)) {
26223             return;
26224         }
26225         if (v.match(/^\{/)) { // allow template editing.
26226             return;
26227         }
26228 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26229         node.removeAttribute(n);
26230         
26231     },
26232     cleanStyle : function(node,  n,v)
26233     {
26234         if (v.match(/expression/)) { //XSS?? should we even bother..
26235             node.removeAttribute(n);
26236             return;
26237         }
26238         
26239         var parts = v.split(/;/);
26240         var clean = [];
26241         
26242         Roo.each(parts, function(p) {
26243             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26244             if (!p.length) {
26245                 return true;
26246             }
26247             var l = p.split(':').shift().replace(/\s+/g,'');
26248             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26249             
26250             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26251                 return true;
26252             }
26253             //Roo.log()
26254             // only allow 'c whitelisted system attributes'
26255             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26256                 return true;
26257             }
26258             
26259             
26260             clean.push(p);
26261             return true;
26262         },this);
26263         if (clean.length) { 
26264             node.setAttribute(n, clean.join(';'));
26265         } else {
26266             node.removeAttribute(n);
26267         }
26268         
26269     }
26270         
26271         
26272         
26273     
26274 });/**
26275  * @class Roo.htmleditor.FilterBlack
26276  * remove blacklisted elements.
26277  * @constructor
26278  * Run a new Blacklisted Filter
26279  * @param {Object} config Configuration options
26280  */
26281
26282 Roo.htmleditor.FilterBlack = function(cfg)
26283 {
26284     Roo.apply(this, cfg);
26285     this.walk(cfg.node);
26286 }
26287
26288 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26289 {
26290     tag : true, // all elements.
26291    
26292     replaceTag : function(n)
26293     {
26294         n.parentNode.removeChild(n);
26295     }
26296 });
26297 /**
26298  * @class Roo.htmleditor.FilterComment
26299  * remove comments.
26300  * @constructor
26301 * Run a new Comments Filter
26302 * @param {Object} config Configuration options
26303  */
26304 Roo.htmleditor.FilterComment = function(cfg)
26305 {
26306     this.walk(cfg.node);
26307 }
26308
26309 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26310 {
26311   
26312     replaceComment : function(n)
26313     {
26314         n.parentNode.removeChild(n);
26315     }
26316 });/**
26317  * @class Roo.htmleditor.FilterKeepChildren
26318  * remove tags but keep children
26319  * @constructor
26320  * Run a new Keep Children Filter
26321  * @param {Object} config Configuration options
26322  */
26323
26324 Roo.htmleditor.FilterKeepChildren = function(cfg)
26325 {
26326     Roo.apply(this, cfg);
26327     if (this.tag === false) {
26328         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26329     }
26330     // hacky?
26331     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26332         this.cleanNamespace = true;
26333     }
26334         
26335     this.walk(cfg.node);
26336 }
26337
26338 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26339 {
26340     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26341   
26342     replaceTag : function(node)
26343     {
26344         // walk children...
26345         //Roo.log(node.tagName);
26346         var ar = Array.from(node.childNodes);
26347         //remove first..
26348         
26349         for (var i = 0; i < ar.length; i++) {
26350             var e = ar[i];
26351             if (e.nodeType == 1) {
26352                 if (
26353                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26354                     || // array and it matches
26355                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26356                     ||
26357                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26358                     ||
26359                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26360                 ) {
26361                     this.replaceTag(ar[i]); // child is blacklisted as well...
26362                     continue;
26363                 }
26364             }
26365         }  
26366         ar = Array.from(node.childNodes);
26367         for (var i = 0; i < ar.length; i++) {
26368          
26369             node.removeChild(ar[i]);
26370             // what if we need to walk these???
26371             node.parentNode.insertBefore(ar[i], node);
26372             if (this.tag !== false) {
26373                 this.walk(ar[i]);
26374                 
26375             }
26376         }
26377         //Roo.log("REMOVE:" + node.tagName);
26378         node.parentNode.removeChild(node);
26379         return false; // don't walk children
26380         
26381         
26382     }
26383 });/**
26384  * @class Roo.htmleditor.FilterParagraph
26385  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26386  * like on 'push' to remove the <p> tags and replace them with line breaks.
26387  * @constructor
26388  * Run a new Paragraph Filter
26389  * @param {Object} config Configuration options
26390  */
26391
26392 Roo.htmleditor.FilterParagraph = function(cfg)
26393 {
26394     // no need to apply config.
26395     this.walk(cfg.node);
26396 }
26397
26398 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26399 {
26400     
26401      
26402     tag : 'P',
26403     
26404      
26405     replaceTag : function(node)
26406     {
26407         
26408         if (node.childNodes.length == 1 &&
26409             node.childNodes[0].nodeType == 3 &&
26410             node.childNodes[0].textContent.trim().length < 1
26411             ) {
26412             // remove and replace with '<BR>';
26413             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26414             return false; // no need to walk..
26415         }
26416         var ar = Array.from(node.childNodes);
26417         for (var i = 0; i < ar.length; i++) {
26418             node.removeChild(ar[i]);
26419             // what if we need to walk these???
26420             node.parentNode.insertBefore(ar[i], node);
26421         }
26422         // now what about this?
26423         // <p> &nbsp; </p>
26424         
26425         // double BR.
26426         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26427         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26428         node.parentNode.removeChild(node);
26429         
26430         return false;
26431
26432     }
26433     
26434 });/**
26435  * @class Roo.htmleditor.FilterSpan
26436  * filter span's with no attributes out..
26437  * @constructor
26438  * Run a new Span Filter
26439  * @param {Object} config Configuration options
26440  */
26441
26442 Roo.htmleditor.FilterSpan = function(cfg)
26443 {
26444     // no need to apply config.
26445     this.walk(cfg.node);
26446 }
26447
26448 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26449 {
26450      
26451     tag : 'SPAN',
26452      
26453  
26454     replaceTag : function(node)
26455     {
26456         if (node.attributes && node.attributes.length > 0) {
26457             return true; // walk if there are any.
26458         }
26459         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26460         return false;
26461      
26462     }
26463     
26464 });/**
26465  * @class Roo.htmleditor.FilterTableWidth
26466   try and remove table width data - as that frequently messes up other stuff.
26467  * 
26468  *      was cleanTableWidths.
26469  *
26470  * Quite often pasting from word etc.. results in tables with column and widths.
26471  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26472  *
26473  * @constructor
26474  * Run a new Table Filter
26475  * @param {Object} config Configuration options
26476  */
26477
26478 Roo.htmleditor.FilterTableWidth = function(cfg)
26479 {
26480     // no need to apply config.
26481     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26482     this.walk(cfg.node);
26483 }
26484
26485 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26486 {
26487      
26488      
26489     
26490     replaceTag: function(node) {
26491         
26492         
26493       
26494         if (node.hasAttribute('width')) {
26495             node.removeAttribute('width');
26496         }
26497         
26498          
26499         if (node.hasAttribute("style")) {
26500             // pretty basic...
26501             
26502             var styles = node.getAttribute("style").split(";");
26503             var nstyle = [];
26504             Roo.each(styles, function(s) {
26505                 if (!s.match(/:/)) {
26506                     return;
26507                 }
26508                 var kv = s.split(":");
26509                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26510                     return;
26511                 }
26512                 // what ever is left... we allow.
26513                 nstyle.push(s);
26514             });
26515             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26516             if (!nstyle.length) {
26517                 node.removeAttribute('style');
26518             }
26519         }
26520         
26521         return true; // continue doing children..
26522     }
26523 });/**
26524  * @class Roo.htmleditor.FilterWord
26525  * try and clean up all the mess that Word generates.
26526  * 
26527  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26528  
26529  * @constructor
26530  * Run a new Span Filter
26531  * @param {Object} config Configuration options
26532  */
26533
26534 Roo.htmleditor.FilterWord = function(cfg)
26535 {
26536     // no need to apply config.
26537     this.replaceDocBullets(cfg.node);
26538     
26539     this.replaceAname(cfg.node);
26540     // this is disabled as the removal is done by other filters;
26541    // this.walk(cfg.node);
26542     
26543     
26544 }
26545
26546 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26547 {
26548     tag: true,
26549      
26550     
26551     /**
26552      * Clean up MS wordisms...
26553      */
26554     replaceTag : function(node)
26555     {
26556          
26557         // no idea what this does - span with text, replaceds with just text.
26558         if(
26559                 node.nodeName == 'SPAN' &&
26560                 !node.hasAttributes() &&
26561                 node.childNodes.length == 1 &&
26562                 node.firstChild.nodeName == "#text"  
26563         ) {
26564             var textNode = node.firstChild;
26565             node.removeChild(textNode);
26566             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26567                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26568             }
26569             node.parentNode.insertBefore(textNode, node);
26570             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26571                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26572             }
26573             
26574             node.parentNode.removeChild(node);
26575             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26576         }
26577         
26578    
26579         
26580         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26581             node.parentNode.removeChild(node);
26582             return false; // dont do chidlren
26583         }
26584         //Roo.log(node.tagName);
26585         // remove - but keep children..
26586         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26587             //Roo.log('-- removed');
26588             while (node.childNodes.length) {
26589                 var cn = node.childNodes[0];
26590                 node.removeChild(cn);
26591                 node.parentNode.insertBefore(cn, node);
26592                 // move node to parent - and clean it..
26593                 if (cn.nodeType == 1) {
26594                     this.replaceTag(cn);
26595                 }
26596                 
26597             }
26598             node.parentNode.removeChild(node);
26599             /// no need to iterate chidlren = it's got none..
26600             //this.iterateChildren(node, this.cleanWord);
26601             return false; // no need to iterate children.
26602         }
26603         // clean styles
26604         if (node.className.length) {
26605             
26606             var cn = node.className.split(/\W+/);
26607             var cna = [];
26608             Roo.each(cn, function(cls) {
26609                 if (cls.match(/Mso[a-zA-Z]+/)) {
26610                     return;
26611                 }
26612                 cna.push(cls);
26613             });
26614             node.className = cna.length ? cna.join(' ') : '';
26615             if (!cna.length) {
26616                 node.removeAttribute("class");
26617             }
26618         }
26619         
26620         if (node.hasAttribute("lang")) {
26621             node.removeAttribute("lang");
26622         }
26623         
26624         if (node.hasAttribute("style")) {
26625             
26626             var styles = node.getAttribute("style").split(";");
26627             var nstyle = [];
26628             Roo.each(styles, function(s) {
26629                 if (!s.match(/:/)) {
26630                     return;
26631                 }
26632                 var kv = s.split(":");
26633                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26634                     return;
26635                 }
26636                 // what ever is left... we allow.
26637                 nstyle.push(s);
26638             });
26639             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26640             if (!nstyle.length) {
26641                 node.removeAttribute('style');
26642             }
26643         }
26644         return true; // do children
26645         
26646         
26647         
26648     },
26649     
26650     styleToObject: function(node)
26651     {
26652         var styles = (node.getAttribute("style") || '').split(";");
26653         var ret = {};
26654         Roo.each(styles, function(s) {
26655             if (!s.match(/:/)) {
26656                 return;
26657             }
26658             var kv = s.split(":");
26659              
26660             // what ever is left... we allow.
26661             ret[kv[0].trim()] = kv[1];
26662         });
26663         return ret;
26664     },
26665     
26666     
26667     replaceAname : function (doc)
26668     {
26669         // replace all the a/name without..
26670         var aa = Array.from(doc.getElementsByTagName('a'));
26671         for (var i = 0; i  < aa.length; i++) {
26672             var a = aa[i];
26673             if (a.hasAttribute("name")) {
26674                 a.removeAttribute("name");
26675             }
26676             if (a.hasAttribute("href")) {
26677                 continue;
26678             }
26679             // reparent children.
26680             this.removeNodeKeepChildren(a);
26681             
26682         }
26683         
26684         
26685         
26686     },
26687
26688     
26689     
26690     replaceDocBullets : function(doc)
26691     {
26692         // this is a bit odd - but it appears some indents use ql-indent-1
26693          //Roo.log(doc.innerHTML);
26694         
26695         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26696         for( var i = 0; i < listpara.length; i ++) {
26697             listpara[i].className = "MsoListParagraph";
26698         }
26699         
26700         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26701         for( var i = 0; i < listpara.length; i ++) {
26702             listpara[i].className = "MsoListParagraph";
26703         }
26704         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26705         for( var i = 0; i < listpara.length; i ++) {
26706             listpara[i].className = "MsoListParagraph";
26707         }
26708         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26709         for( var i = 0; i < listpara.length; i ++) {
26710             listpara[i].className = "MsoListParagraph";
26711         }
26712         
26713         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26714         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26715         for( var i = 0; i < htwo.length; i ++) {
26716             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26717                 htwo[i].className = "MsoListParagraph";
26718             }
26719         }
26720         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26721         for( var i = 0; i < listpara.length; i ++) {
26722             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26723                 listpara[i].className = "MsoListParagraph";
26724             } else {
26725                 listpara[i].className = "MsoNormalx";
26726             }
26727         }
26728        
26729         listpara = doc.getElementsByClassName('MsoListParagraph');
26730         // Roo.log(doc.innerHTML);
26731         
26732         
26733         
26734         while(listpara.length) {
26735             
26736             this.replaceDocBullet(listpara.item(0));
26737         }
26738       
26739     },
26740     
26741      
26742     
26743     replaceDocBullet : function(p)
26744     {
26745         // gather all the siblings.
26746         var ns = p,
26747             parent = p.parentNode,
26748             doc = parent.ownerDocument,
26749             items = [];
26750             
26751         var listtype = 'ul';   
26752         while (ns) {
26753             if (ns.nodeType != 1) {
26754                 ns = ns.nextSibling;
26755                 continue;
26756             }
26757             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26758                 break;
26759             }
26760             var spans = ns.getElementsByTagName('span');
26761             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26762                 items.push(ns);
26763                 ns = ns.nextSibling;
26764                 has_list = true;
26765                 if (spans.length && spans[0].hasAttribute('style')) {
26766                     var  style = this.styleToObject(spans[0]);
26767                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26768                         listtype = 'ol';
26769                     }
26770                 }
26771                 
26772                 continue;
26773             }
26774             var spans = ns.getElementsByTagName('span');
26775             if (!spans.length) {
26776                 break;
26777             }
26778             var has_list  = false;
26779             for(var i = 0; i < spans.length; i++) {
26780                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26781                     has_list = true;
26782                     break;
26783                 }
26784             }
26785             if (!has_list) {
26786                 break;
26787             }
26788             items.push(ns);
26789             ns = ns.nextSibling;
26790             
26791             
26792         }
26793         if (!items.length) {
26794             ns.className = "";
26795             return;
26796         }
26797         
26798         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26799         parent.insertBefore(ul, p);
26800         var lvl = 0;
26801         var stack = [ ul ];
26802         var last_li = false;
26803         
26804         var margin_to_depth = {};
26805         max_margins = -1;
26806         
26807         items.forEach(function(n, ipos) {
26808             //Roo.log("got innertHMLT=" + n.innerHTML);
26809             
26810             var spans = n.getElementsByTagName('span');
26811             if (!spans.length) {
26812                 //Roo.log("No spans found");
26813                  
26814                 parent.removeChild(n);
26815                 
26816                 
26817                 return; // skip it...
26818             }
26819            
26820                 
26821             var num = 1;
26822             var style = {};
26823             for(var i = 0; i < spans.length; i++) {
26824             
26825                 style = this.styleToObject(spans[i]);
26826                 if (typeof(style['mso-list']) == 'undefined') {
26827                     continue;
26828                 }
26829                 if (listtype == 'ol') {
26830                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26831                 }
26832                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26833                 break;
26834             }
26835             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26836             style = this.styleToObject(n); // mo-list is from the parent node.
26837             if (typeof(style['mso-list']) == 'undefined') {
26838                 //Roo.log("parent is missing level");
26839                   
26840                 parent.removeChild(n);
26841                  
26842                 return;
26843             }
26844             
26845             var margin = style['margin-left'];
26846             if (typeof(margin_to_depth[margin]) == 'undefined') {
26847                 max_margins++;
26848                 margin_to_depth[margin] = max_margins;
26849             }
26850             nlvl = margin_to_depth[margin] ;
26851              
26852             if (nlvl > lvl) {
26853                 //new indent
26854                 var nul = doc.createElement(listtype); // what about number lists...
26855                 if (!last_li) {
26856                     last_li = doc.createElement('li');
26857                     stack[lvl].appendChild(last_li);
26858                 }
26859                 last_li.appendChild(nul);
26860                 stack[nlvl] = nul;
26861                 
26862             }
26863             lvl = nlvl;
26864             
26865             // not starting at 1..
26866             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26867                 stack[nlvl].setAttribute("start", num);
26868             }
26869             
26870             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26871             last_li = nli;
26872             nli.innerHTML = n.innerHTML;
26873             //Roo.log("innerHTML = " + n.innerHTML);
26874             parent.removeChild(n);
26875             
26876              
26877              
26878             
26879         },this);
26880         
26881         
26882         
26883         
26884     }
26885     
26886     
26887     
26888 });
26889 /**
26890  * @class Roo.htmleditor.FilterStyleToTag
26891  * part of the word stuff... - certain 'styles' should be converted to tags.
26892  * eg.
26893  *   font-weight: bold -> bold
26894  *   ?? super / subscrit etc..
26895  * 
26896  * @constructor
26897 * Run a new style to tag filter.
26898 * @param {Object} config Configuration options
26899  */
26900 Roo.htmleditor.FilterStyleToTag = function(cfg)
26901 {
26902     
26903     this.tags = {
26904         B  : [ 'fontWeight' , 'bold'],
26905         I :  [ 'fontStyle' , 'italic'],
26906         //pre :  [ 'font-style' , 'italic'],
26907         // h1.. h6 ?? font-size?
26908         SUP : [ 'verticalAlign' , 'super' ],
26909         SUB : [ 'verticalAlign' , 'sub' ]
26910         
26911         
26912     };
26913     
26914     Roo.apply(this, cfg);
26915      
26916     
26917     this.walk(cfg.node);
26918     
26919     
26920     
26921 }
26922
26923
26924 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26925 {
26926     tag: true, // all tags
26927     
26928     tags : false,
26929     
26930     
26931     replaceTag : function(node)
26932     {
26933         
26934         
26935         if (node.getAttribute("style") === null) {
26936             return true;
26937         }
26938         var inject = [];
26939         for (var k in this.tags) {
26940             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26941                 inject.push(k);
26942                 node.style.removeProperty(this.tags[k][0]);
26943             }
26944         }
26945         if (!inject.length) {
26946             return true; 
26947         }
26948         var cn = Array.from(node.childNodes);
26949         var nn = node;
26950         Roo.each(inject, function(t) {
26951             var nc = node.ownerDocument.createElement(t);
26952             nn.appendChild(nc);
26953             nn = nc;
26954         });
26955         for(var i = 0;i < cn.length;cn++) {
26956             node.removeChild(cn[i]);
26957             nn.appendChild(cn[i]);
26958         }
26959         return true /// iterate thru
26960     }
26961     
26962 })/**
26963  * @class Roo.htmleditor.FilterLongBr
26964  * BR/BR/BR - keep a maximum of 2...
26965  * @constructor
26966  * Run a new Long BR Filter
26967  * @param {Object} config Configuration options
26968  */
26969
26970 Roo.htmleditor.FilterLongBr = function(cfg)
26971 {
26972     // no need to apply config.
26973     this.walk(cfg.node);
26974 }
26975
26976 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26977 {
26978     
26979      
26980     tag : 'BR',
26981     
26982      
26983     replaceTag : function(node)
26984     {
26985         
26986         var ps = node.nextSibling;
26987         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26988             ps = ps.nextSibling;
26989         }
26990         
26991         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26992             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26993             return false;
26994         }
26995         
26996         if (!ps || ps.nodeType != 1) {
26997             return false;
26998         }
26999         
27000         if (!ps || ps.tagName != 'BR') {
27001            
27002             return false;
27003         }
27004         
27005         
27006         
27007         
27008         
27009         if (!node.previousSibling) {
27010             return false;
27011         }
27012         var ps = node.previousSibling;
27013         
27014         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27015             ps = ps.previousSibling;
27016         }
27017         if (!ps || ps.nodeType != 1) {
27018             return false;
27019         }
27020         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27021         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27022             return false;
27023         }
27024         
27025         node.parentNode.removeChild(node); // remove me...
27026         
27027         return false; // no need to do children
27028
27029     }
27030     
27031 }); 
27032
27033 /**
27034  * @class Roo.htmleditor.FilterBlock
27035  * removes id / data-block and contenteditable that are associated with blocks
27036  * usage should be done on a cloned copy of the dom
27037  * @constructor
27038 * Run a new Attribute Filter { node : xxxx }}
27039 * @param {Object} config Configuration options
27040  */
27041 Roo.htmleditor.FilterBlock = function(cfg)
27042 {
27043     Roo.apply(this, cfg);
27044     var qa = cfg.node.querySelectorAll;
27045     this.removeAttributes('data-block');
27046     this.removeAttributes('contenteditable');
27047     this.removeAttributes('id');
27048     
27049 }
27050
27051 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27052 {
27053     node: true, // all tags
27054      
27055      
27056     removeAttributes : function(attr)
27057     {
27058         var ar = this.node.querySelectorAll('*[' + attr + ']');
27059         for (var i =0;i<ar.length;i++) {
27060             ar[i].removeAttribute(attr);
27061         }
27062     }
27063         
27064         
27065         
27066     
27067 });
27068 /***
27069  * This is based loosely on tinymce 
27070  * @class Roo.htmleditor.TidySerializer
27071  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27072  * @constructor
27073  * @method Serializer
27074  * @param {Object} settings Name/value settings object.
27075  */
27076
27077
27078 Roo.htmleditor.TidySerializer = function(settings)
27079 {
27080     Roo.apply(this, settings);
27081     
27082     this.writer = new Roo.htmleditor.TidyWriter(settings);
27083     
27084     
27085
27086 };
27087 Roo.htmleditor.TidySerializer.prototype = {
27088     
27089     /**
27090      * @param {boolean} inner do the inner of the node.
27091      */
27092     inner : false,
27093     
27094     writer : false,
27095     
27096     /**
27097     * Serializes the specified node into a string.
27098     *
27099     * @example
27100     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27101     * @method serialize
27102     * @param {DomElement} node Node instance to serialize.
27103     * @return {String} String with HTML based on DOM tree.
27104     */
27105     serialize : function(node) {
27106         
27107         // = settings.validate;
27108         var writer = this.writer;
27109         var self  = this;
27110         this.handlers = {
27111             // #text
27112             3: function(node) {
27113                 
27114                 writer.text(node.nodeValue, node);
27115             },
27116             // #comment
27117             8: function(node) {
27118                 writer.comment(node.nodeValue);
27119             },
27120             // Processing instruction
27121             7: function(node) {
27122                 writer.pi(node.name, node.nodeValue);
27123             },
27124             // Doctype
27125             10: function(node) {
27126                 writer.doctype(node.nodeValue);
27127             },
27128             // CDATA
27129             4: function(node) {
27130                 writer.cdata(node.nodeValue);
27131             },
27132             // Document fragment
27133             11: function(node) {
27134                 node = node.firstChild;
27135                 if (!node) {
27136                     return;
27137                 }
27138                 while(node) {
27139                     self.walk(node);
27140                     node = node.nextSibling
27141                 }
27142             }
27143         };
27144         writer.reset();
27145         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27146         return writer.getContent();
27147     },
27148
27149     walk: function(node)
27150     {
27151         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27152             handler = this.handlers[node.nodeType];
27153             
27154         if (handler) {
27155             handler(node);
27156             return;
27157         }
27158     
27159         var name = node.nodeName;
27160         var isEmpty = node.childNodes.length < 1;
27161       
27162         var writer = this.writer;
27163         var attrs = node.attributes;
27164         // Sort attributes
27165         
27166         writer.start(node.nodeName, attrs, isEmpty, node);
27167         if (isEmpty) {
27168             return;
27169         }
27170         node = node.firstChild;
27171         if (!node) {
27172             writer.end(name);
27173             return;
27174         }
27175         while (node) {
27176             this.walk(node);
27177             node = node.nextSibling;
27178         }
27179         writer.end(name);
27180         
27181     
27182     }
27183     // Serialize element and treat all non elements as fragments
27184    
27185 }; 
27186
27187 /***
27188  * This is based loosely on tinymce 
27189  * @class Roo.htmleditor.TidyWriter
27190  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27191  *
27192  * Known issues?
27193  * - not tested much with 'PRE' formated elements.
27194  * 
27195  *
27196  *
27197  */
27198
27199 Roo.htmleditor.TidyWriter = function(settings)
27200 {
27201     
27202     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27203     Roo.apply(this, settings);
27204     this.html = [];
27205     this.state = [];
27206      
27207     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27208   
27209 }
27210 Roo.htmleditor.TidyWriter.prototype = {
27211
27212  
27213     state : false,
27214     
27215     indent :  '  ',
27216     
27217     // part of state...
27218     indentstr : '',
27219     in_pre: false,
27220     in_inline : false,
27221     last_inline : false,
27222     encode : false,
27223      
27224     
27225             /**
27226     * Writes the a start element such as <p id="a">.
27227     *
27228     * @method start
27229     * @param {String} name Name of the element.
27230     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27231     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27232     */
27233     start: function(name, attrs, empty, node)
27234     {
27235         var i, l, attr, value;
27236         
27237         // there are some situations where adding line break && indentation will not work. will not work.
27238         // <span / b / i ... formating?
27239         
27240         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27241         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27242         
27243         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27244         
27245         var add_lb = name == 'BR' ? false : in_inline;
27246         
27247         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27248             i_inline = false;
27249         }
27250
27251         var indentstr =  this.indentstr;
27252         
27253         // e_inline = elements that can be inline, but still allow \n before and after?
27254         // only 'BR' ??? any others?
27255         
27256         // ADD LINE BEFORE tage
27257         if (!this.in_pre) {
27258             if (in_inline) {
27259                 //code
27260                 if (name == 'BR') {
27261                     this.addLine();
27262                 } else if (this.lastElementEndsWS()) {
27263                     this.addLine();
27264                 } else{
27265                     // otherwise - no new line. (and dont indent.)
27266                     indentstr = '';
27267                 }
27268                 
27269             } else {
27270                 this.addLine();
27271             }
27272         } else {
27273             indentstr = '';
27274         }
27275         
27276         this.html.push(indentstr + '<', name.toLowerCase());
27277         
27278         if (attrs) {
27279             for (i = 0, l = attrs.length; i < l; i++) {
27280                 attr = attrs[i];
27281                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27282             }
27283         }
27284      
27285         if (empty) {
27286             if (is_short) {
27287                 this.html[this.html.length] = '/>';
27288             } else {
27289                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27290             }
27291             var e_inline = name == 'BR' ? false : this.in_inline;
27292             
27293             if (!e_inline && !this.in_pre) {
27294                 this.addLine();
27295             }
27296             return;
27297         
27298         }
27299         // not empty..
27300         this.html[this.html.length] = '>';
27301         
27302         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27303         /*
27304         if (!in_inline && !in_pre) {
27305             var cn = node.firstChild;
27306             while(cn) {
27307                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27308                     in_inline = true
27309                     break;
27310                 }
27311                 cn = cn.nextSibling;
27312             }
27313              
27314         }
27315         */
27316         
27317         
27318         this.pushState({
27319             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27320             in_pre : in_pre,
27321             in_inline :  in_inline
27322         });
27323         // add a line after if we are not in a
27324         
27325         if (!in_inline && !in_pre) {
27326             this.addLine();
27327         }
27328         
27329             
27330          
27331         
27332     },
27333     
27334     lastElementEndsWS : function()
27335     {
27336         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27337         if (value === false) {
27338             return true;
27339         }
27340         return value.match(/\s+$/);
27341         
27342     },
27343     
27344     /**
27345      * Writes the a end element such as </p>.
27346      *
27347      * @method end
27348      * @param {String} name Name of the element.
27349      */
27350     end: function(name) {
27351         var value;
27352         this.popState();
27353         var indentstr = '';
27354         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27355         
27356         if (!this.in_pre && !in_inline) {
27357             this.addLine();
27358             indentstr  = this.indentstr;
27359         }
27360         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27361         this.last_inline = in_inline;
27362         
27363         // pop the indent state..
27364     },
27365     /**
27366      * Writes a text node.
27367      *
27368      * In pre - we should not mess with the contents.
27369      * 
27370      *
27371      * @method text
27372      * @param {String} text String to write out.
27373      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27374      */
27375     text: function(in_text, node)
27376     {
27377         // if not in whitespace critical
27378         if (in_text.length < 1) {
27379             return;
27380         }
27381         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27382         
27383         if (this.in_pre) {
27384             this.html[this.html.length] =  text;
27385             return;   
27386         }
27387         
27388         if (this.in_inline) {
27389             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27390             if (text != ' ') {
27391                 text = text.replace(/\s+/,' ');  // all white space to single white space
27392                 
27393                     
27394                 // if next tag is '<BR>', then we can trim right..
27395                 if (node.nextSibling &&
27396                     node.nextSibling.nodeType == 1 &&
27397                     node.nextSibling.nodeName == 'BR' )
27398                 {
27399                     text = text.replace(/\s+$/g,'');
27400                 }
27401                 // if previous tag was a BR, we can also trim..
27402                 if (node.previousSibling &&
27403                     node.previousSibling.nodeType == 1 &&
27404                     node.previousSibling.nodeName == 'BR' )
27405                 {
27406                     text = this.indentstr +  text.replace(/^\s+/g,'');
27407                 }
27408                 if (text.match(/\n/)) {
27409                     text = text.replace(
27410                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27411                     );
27412                     // remoeve the last whitespace / line break.
27413                     text = text.replace(/\n\s+$/,'');
27414                 }
27415                 // repace long lines
27416                 
27417             }
27418              
27419             this.html[this.html.length] =  text;
27420             return;   
27421         }
27422         // see if previous element was a inline element.
27423         var indentstr = this.indentstr;
27424    
27425         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27426         
27427         // should trim left?
27428         if (node.previousSibling &&
27429             node.previousSibling.nodeType == 1 &&
27430             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27431         {
27432             indentstr = '';
27433             
27434         } else {
27435             this.addLine();
27436             text = text.replace(/^\s+/,''); // trim left
27437           
27438         }
27439         // should trim right?
27440         if (node.nextSibling &&
27441             node.nextSibling.nodeType == 1 &&
27442             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27443         {
27444           // noop
27445             
27446         }  else {
27447             text = text.replace(/\s+$/,''); // trim right
27448         }
27449          
27450               
27451         
27452         
27453         
27454         if (text.length < 1) {
27455             return;
27456         }
27457         if (!text.match(/\n/)) {
27458             this.html.push(indentstr + text);
27459             return;
27460         }
27461         
27462         text = this.indentstr + text.replace(
27463             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27464         );
27465         // remoeve the last whitespace / line break.
27466         text = text.replace(/\s+$/,''); 
27467         
27468         this.html.push(text);
27469         
27470         // split and indent..
27471         
27472         
27473     },
27474     /**
27475      * Writes a cdata node such as <![CDATA[data]]>.
27476      *
27477      * @method cdata
27478      * @param {String} text String to write out inside the cdata.
27479      */
27480     cdata: function(text) {
27481         this.html.push('<![CDATA[', text, ']]>');
27482     },
27483     /**
27484     * Writes a comment node such as <!-- Comment -->.
27485     *
27486     * @method cdata
27487     * @param {String} text String to write out inside the comment.
27488     */
27489    comment: function(text) {
27490        this.html.push('<!--', text, '-->');
27491    },
27492     /**
27493      * Writes a PI node such as <?xml attr="value" ?>.
27494      *
27495      * @method pi
27496      * @param {String} name Name of the pi.
27497      * @param {String} text String to write out inside the pi.
27498      */
27499     pi: function(name, text) {
27500         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27501         this.indent != '' && this.html.push('\n');
27502     },
27503     /**
27504      * Writes a doctype node such as <!DOCTYPE data>.
27505      *
27506      * @method doctype
27507      * @param {String} text String to write out inside the doctype.
27508      */
27509     doctype: function(text) {
27510         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27511     },
27512     /**
27513      * Resets the internal buffer if one wants to reuse the writer.
27514      *
27515      * @method reset
27516      */
27517     reset: function() {
27518         this.html.length = 0;
27519         this.state = [];
27520         this.pushState({
27521             indentstr : '',
27522             in_pre : false, 
27523             in_inline : false
27524         })
27525     },
27526     /**
27527      * Returns the contents that got serialized.
27528      *
27529      * @method getContent
27530      * @return {String} HTML contents that got written down.
27531      */
27532     getContent: function() {
27533         return this.html.join('').replace(/\n$/, '');
27534     },
27535     
27536     pushState : function(cfg)
27537     {
27538         this.state.push(cfg);
27539         Roo.apply(this, cfg);
27540     },
27541     
27542     popState : function()
27543     {
27544         if (this.state.length < 1) {
27545             return; // nothing to push
27546         }
27547         var cfg = {
27548             in_pre: false,
27549             indentstr : ''
27550         };
27551         this.state.pop();
27552         if (this.state.length > 0) {
27553             cfg = this.state[this.state.length-1]; 
27554         }
27555         Roo.apply(this, cfg);
27556     },
27557     
27558     addLine: function()
27559     {
27560         if (this.html.length < 1) {
27561             return;
27562         }
27563         
27564         
27565         var value = this.html[this.html.length - 1];
27566         if (value.length > 0 && '\n' !== value) {
27567             this.html.push('\n');
27568         }
27569     }
27570     
27571     
27572 //'pre script noscript style textarea video audio iframe object code'
27573 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
27574 // inline 
27575 };
27576
27577 Roo.htmleditor.TidyWriter.inline_elements = [
27578         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
27579         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
27580 ];
27581 Roo.htmleditor.TidyWriter.shortend_elements = [
27582     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
27583     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
27584 ];
27585
27586 Roo.htmleditor.TidyWriter.whitespace_elements = [
27587     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
27588 ];/***
27589  * This is based loosely on tinymce 
27590  * @class Roo.htmleditor.TidyEntities
27591  * @static
27592  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27593  *
27594  * Not 100% sure this is actually used or needed.
27595  */
27596
27597 Roo.htmleditor.TidyEntities = {
27598     
27599     /**
27600      * initialize data..
27601      */
27602     init : function (){
27603      
27604         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
27605        
27606     },
27607
27608
27609     buildEntitiesLookup: function(items, radix) {
27610         var i, chr, entity, lookup = {};
27611         if (!items) {
27612             return {};
27613         }
27614         items = typeof(items) == 'string' ? items.split(',') : items;
27615         radix = radix || 10;
27616         // Build entities lookup table
27617         for (i = 0; i < items.length; i += 2) {
27618             chr = String.fromCharCode(parseInt(items[i], radix));
27619             // Only add non base entities
27620             if (!this.baseEntities[chr]) {
27621                 entity = '&' + items[i + 1] + ';';
27622                 lookup[chr] = entity;
27623                 lookup[entity] = chr;
27624             }
27625         }
27626         return lookup;
27627         
27628     },
27629     
27630     asciiMap : {
27631             128: '€',
27632             130: '‚',
27633             131: 'ƒ',
27634             132: '„',
27635             133: '…',
27636             134: '†',
27637             135: '‡',
27638             136: 'ˆ',
27639             137: '‰',
27640             138: 'Š',
27641             139: '‹',
27642             140: 'Œ',
27643             142: 'Ž',
27644             145: '‘',
27645             146: '’',
27646             147: '“',
27647             148: '”',
27648             149: '•',
27649             150: '–',
27650             151: '—',
27651             152: '˜',
27652             153: '™',
27653             154: 'š',
27654             155: '›',
27655             156: 'œ',
27656             158: 'ž',
27657             159: 'Ÿ'
27658     },
27659     // Raw entities
27660     baseEntities : {
27661         '"': '&quot;',
27662         // Needs to be escaped since the YUI compressor would otherwise break the code
27663         '\'': '&#39;',
27664         '<': '&lt;',
27665         '>': '&gt;',
27666         '&': '&amp;',
27667         '`': '&#96;'
27668     },
27669     // Reverse lookup table for raw entities
27670     reverseEntities : {
27671         '&lt;': '<',
27672         '&gt;': '>',
27673         '&amp;': '&',
27674         '&quot;': '"',
27675         '&apos;': '\''
27676     },
27677     
27678     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27679     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27680     rawCharsRegExp : /[<>&\"\']/g,
27681     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
27682     namedEntities  : false,
27683     namedEntitiesData : [ 
27684         '50',
27685         'nbsp',
27686         '51',
27687         'iexcl',
27688         '52',
27689         'cent',
27690         '53',
27691         'pound',
27692         '54',
27693         'curren',
27694         '55',
27695         'yen',
27696         '56',
27697         'brvbar',
27698         '57',
27699         'sect',
27700         '58',
27701         'uml',
27702         '59',
27703         'copy',
27704         '5a',
27705         'ordf',
27706         '5b',
27707         'laquo',
27708         '5c',
27709         'not',
27710         '5d',
27711         'shy',
27712         '5e',
27713         'reg',
27714         '5f',
27715         'macr',
27716         '5g',
27717         'deg',
27718         '5h',
27719         'plusmn',
27720         '5i',
27721         'sup2',
27722         '5j',
27723         'sup3',
27724         '5k',
27725         'acute',
27726         '5l',
27727         'micro',
27728         '5m',
27729         'para',
27730         '5n',
27731         'middot',
27732         '5o',
27733         'cedil',
27734         '5p',
27735         'sup1',
27736         '5q',
27737         'ordm',
27738         '5r',
27739         'raquo',
27740         '5s',
27741         'frac14',
27742         '5t',
27743         'frac12',
27744         '5u',
27745         'frac34',
27746         '5v',
27747         'iquest',
27748         '60',
27749         'Agrave',
27750         '61',
27751         'Aacute',
27752         '62',
27753         'Acirc',
27754         '63',
27755         'Atilde',
27756         '64',
27757         'Auml',
27758         '65',
27759         'Aring',
27760         '66',
27761         'AElig',
27762         '67',
27763         'Ccedil',
27764         '68',
27765         'Egrave',
27766         '69',
27767         'Eacute',
27768         '6a',
27769         'Ecirc',
27770         '6b',
27771         'Euml',
27772         '6c',
27773         'Igrave',
27774         '6d',
27775         'Iacute',
27776         '6e',
27777         'Icirc',
27778         '6f',
27779         'Iuml',
27780         '6g',
27781         'ETH',
27782         '6h',
27783         'Ntilde',
27784         '6i',
27785         'Ograve',
27786         '6j',
27787         'Oacute',
27788         '6k',
27789         'Ocirc',
27790         '6l',
27791         'Otilde',
27792         '6m',
27793         'Ouml',
27794         '6n',
27795         'times',
27796         '6o',
27797         'Oslash',
27798         '6p',
27799         'Ugrave',
27800         '6q',
27801         'Uacute',
27802         '6r',
27803         'Ucirc',
27804         '6s',
27805         'Uuml',
27806         '6t',
27807         'Yacute',
27808         '6u',
27809         'THORN',
27810         '6v',
27811         'szlig',
27812         '70',
27813         'agrave',
27814         '71',
27815         'aacute',
27816         '72',
27817         'acirc',
27818         '73',
27819         'atilde',
27820         '74',
27821         'auml',
27822         '75',
27823         'aring',
27824         '76',
27825         'aelig',
27826         '77',
27827         'ccedil',
27828         '78',
27829         'egrave',
27830         '79',
27831         'eacute',
27832         '7a',
27833         'ecirc',
27834         '7b',
27835         'euml',
27836         '7c',
27837         'igrave',
27838         '7d',
27839         'iacute',
27840         '7e',
27841         'icirc',
27842         '7f',
27843         'iuml',
27844         '7g',
27845         'eth',
27846         '7h',
27847         'ntilde',
27848         '7i',
27849         'ograve',
27850         '7j',
27851         'oacute',
27852         '7k',
27853         'ocirc',
27854         '7l',
27855         'otilde',
27856         '7m',
27857         'ouml',
27858         '7n',
27859         'divide',
27860         '7o',
27861         'oslash',
27862         '7p',
27863         'ugrave',
27864         '7q',
27865         'uacute',
27866         '7r',
27867         'ucirc',
27868         '7s',
27869         'uuml',
27870         '7t',
27871         'yacute',
27872         '7u',
27873         'thorn',
27874         '7v',
27875         'yuml',
27876         'ci',
27877         'fnof',
27878         'sh',
27879         'Alpha',
27880         'si',
27881         'Beta',
27882         'sj',
27883         'Gamma',
27884         'sk',
27885         'Delta',
27886         'sl',
27887         'Epsilon',
27888         'sm',
27889         'Zeta',
27890         'sn',
27891         'Eta',
27892         'so',
27893         'Theta',
27894         'sp',
27895         'Iota',
27896         'sq',
27897         'Kappa',
27898         'sr',
27899         'Lambda',
27900         'ss',
27901         'Mu',
27902         'st',
27903         'Nu',
27904         'su',
27905         'Xi',
27906         'sv',
27907         'Omicron',
27908         't0',
27909         'Pi',
27910         't1',
27911         'Rho',
27912         't3',
27913         'Sigma',
27914         't4',
27915         'Tau',
27916         't5',
27917         'Upsilon',
27918         't6',
27919         'Phi',
27920         't7',
27921         'Chi',
27922         't8',
27923         'Psi',
27924         't9',
27925         'Omega',
27926         'th',
27927         'alpha',
27928         'ti',
27929         'beta',
27930         'tj',
27931         'gamma',
27932         'tk',
27933         'delta',
27934         'tl',
27935         'epsilon',
27936         'tm',
27937         'zeta',
27938         'tn',
27939         'eta',
27940         'to',
27941         'theta',
27942         'tp',
27943         'iota',
27944         'tq',
27945         'kappa',
27946         'tr',
27947         'lambda',
27948         'ts',
27949         'mu',
27950         'tt',
27951         'nu',
27952         'tu',
27953         'xi',
27954         'tv',
27955         'omicron',
27956         'u0',
27957         'pi',
27958         'u1',
27959         'rho',
27960         'u2',
27961         'sigmaf',
27962         'u3',
27963         'sigma',
27964         'u4',
27965         'tau',
27966         'u5',
27967         'upsilon',
27968         'u6',
27969         'phi',
27970         'u7',
27971         'chi',
27972         'u8',
27973         'psi',
27974         'u9',
27975         'omega',
27976         'uh',
27977         'thetasym',
27978         'ui',
27979         'upsih',
27980         'um',
27981         'piv',
27982         '812',
27983         'bull',
27984         '816',
27985         'hellip',
27986         '81i',
27987         'prime',
27988         '81j',
27989         'Prime',
27990         '81u',
27991         'oline',
27992         '824',
27993         'frasl',
27994         '88o',
27995         'weierp',
27996         '88h',
27997         'image',
27998         '88s',
27999         'real',
28000         '892',
28001         'trade',
28002         '89l',
28003         'alefsym',
28004         '8cg',
28005         'larr',
28006         '8ch',
28007         'uarr',
28008         '8ci',
28009         'rarr',
28010         '8cj',
28011         'darr',
28012         '8ck',
28013         'harr',
28014         '8dl',
28015         'crarr',
28016         '8eg',
28017         'lArr',
28018         '8eh',
28019         'uArr',
28020         '8ei',
28021         'rArr',
28022         '8ej',
28023         'dArr',
28024         '8ek',
28025         'hArr',
28026         '8g0',
28027         'forall',
28028         '8g2',
28029         'part',
28030         '8g3',
28031         'exist',
28032         '8g5',
28033         'empty',
28034         '8g7',
28035         'nabla',
28036         '8g8',
28037         'isin',
28038         '8g9',
28039         'notin',
28040         '8gb',
28041         'ni',
28042         '8gf',
28043         'prod',
28044         '8gh',
28045         'sum',
28046         '8gi',
28047         'minus',
28048         '8gn',
28049         'lowast',
28050         '8gq',
28051         'radic',
28052         '8gt',
28053         'prop',
28054         '8gu',
28055         'infin',
28056         '8h0',
28057         'ang',
28058         '8h7',
28059         'and',
28060         '8h8',
28061         'or',
28062         '8h9',
28063         'cap',
28064         '8ha',
28065         'cup',
28066         '8hb',
28067         'int',
28068         '8hk',
28069         'there4',
28070         '8hs',
28071         'sim',
28072         '8i5',
28073         'cong',
28074         '8i8',
28075         'asymp',
28076         '8j0',
28077         'ne',
28078         '8j1',
28079         'equiv',
28080         '8j4',
28081         'le',
28082         '8j5',
28083         'ge',
28084         '8k2',
28085         'sub',
28086         '8k3',
28087         'sup',
28088         '8k4',
28089         'nsub',
28090         '8k6',
28091         'sube',
28092         '8k7',
28093         'supe',
28094         '8kl',
28095         'oplus',
28096         '8kn',
28097         'otimes',
28098         '8l5',
28099         'perp',
28100         '8m5',
28101         'sdot',
28102         '8o8',
28103         'lceil',
28104         '8o9',
28105         'rceil',
28106         '8oa',
28107         'lfloor',
28108         '8ob',
28109         'rfloor',
28110         '8p9',
28111         'lang',
28112         '8pa',
28113         'rang',
28114         '9ea',
28115         'loz',
28116         '9j0',
28117         'spades',
28118         '9j3',
28119         'clubs',
28120         '9j5',
28121         'hearts',
28122         '9j6',
28123         'diams',
28124         'ai',
28125         'OElig',
28126         'aj',
28127         'oelig',
28128         'b0',
28129         'Scaron',
28130         'b1',
28131         'scaron',
28132         'bo',
28133         'Yuml',
28134         'm6',
28135         'circ',
28136         'ms',
28137         'tilde',
28138         '802',
28139         'ensp',
28140         '803',
28141         'emsp',
28142         '809',
28143         'thinsp',
28144         '80c',
28145         'zwnj',
28146         '80d',
28147         'zwj',
28148         '80e',
28149         'lrm',
28150         '80f',
28151         'rlm',
28152         '80j',
28153         'ndash',
28154         '80k',
28155         'mdash',
28156         '80o',
28157         'lsquo',
28158         '80p',
28159         'rsquo',
28160         '80q',
28161         'sbquo',
28162         '80s',
28163         'ldquo',
28164         '80t',
28165         'rdquo',
28166         '80u',
28167         'bdquo',
28168         '810',
28169         'dagger',
28170         '811',
28171         'Dagger',
28172         '81g',
28173         'permil',
28174         '81p',
28175         'lsaquo',
28176         '81q',
28177         'rsaquo',
28178         '85c',
28179         'euro'
28180     ],
28181
28182          
28183     /**
28184      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28185      *
28186      * @method encodeRaw
28187      * @param {String} text Text to encode.
28188      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28189      * @return {String} Entity encoded text.
28190      */
28191     encodeRaw: function(text, attr)
28192     {
28193         var t = this;
28194         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28195             return t.baseEntities[chr] || chr;
28196         });
28197     },
28198     /**
28199      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28200      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28201      * and is exposed as the DOMUtils.encode function.
28202      *
28203      * @method encodeAllRaw
28204      * @param {String} text Text to encode.
28205      * @return {String} Entity encoded text.
28206      */
28207     encodeAllRaw: function(text) {
28208         var t = this;
28209         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28210             return t.baseEntities[chr] || chr;
28211         });
28212     },
28213     /**
28214      * Encodes the specified string using numeric entities. The core entities will be
28215      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28216      *
28217      * @method encodeNumeric
28218      * @param {String} text Text to encode.
28219      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28220      * @return {String} Entity encoded text.
28221      */
28222     encodeNumeric: function(text, attr) {
28223         var t = this;
28224         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28225             // Multi byte sequence convert it to a single entity
28226             if (chr.length > 1) {
28227                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28228             }
28229             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28230         });
28231     },
28232     /**
28233      * Encodes the specified string using named entities. The core entities will be encoded
28234      * as named ones but all non lower ascii characters will be encoded into named entities.
28235      *
28236      * @method encodeNamed
28237      * @param {String} text Text to encode.
28238      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28239      * @param {Object} entities Optional parameter with entities to use.
28240      * @return {String} Entity encoded text.
28241      */
28242     encodeNamed: function(text, attr, entities) {
28243         var t = this;
28244         entities = entities || this.namedEntities;
28245         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28246             return t.baseEntities[chr] || entities[chr] || chr;
28247         });
28248     },
28249     /**
28250      * Returns an encode function based on the name(s) and it's optional entities.
28251      *
28252      * @method getEncodeFunc
28253      * @param {String} name Comma separated list of encoders for example named,numeric.
28254      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28255      * @return {function} Encode function to be used.
28256      */
28257     getEncodeFunc: function(name, entities) {
28258         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28259         var t = this;
28260         function encodeNamedAndNumeric(text, attr) {
28261             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28262                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28263             });
28264         }
28265
28266         function encodeCustomNamed(text, attr) {
28267             return t.encodeNamed(text, attr, entities);
28268         }
28269         // Replace + with , to be compatible with previous TinyMCE versions
28270         name = this.makeMap(name.replace(/\+/g, ','));
28271         // Named and numeric encoder
28272         if (name.named && name.numeric) {
28273             return this.encodeNamedAndNumeric;
28274         }
28275         // Named encoder
28276         if (name.named) {
28277             // Custom names
28278             if (entities) {
28279                 return encodeCustomNamed;
28280             }
28281             return this.encodeNamed;
28282         }
28283         // Numeric
28284         if (name.numeric) {
28285             return this.encodeNumeric;
28286         }
28287         // Raw encoder
28288         return this.encodeRaw;
28289     },
28290     /**
28291      * Decodes the specified string, this will replace entities with raw UTF characters.
28292      *
28293      * @method decode
28294      * @param {String} text Text to entity decode.
28295      * @return {String} Entity decoded string.
28296      */
28297     decode: function(text)
28298     {
28299         var  t = this;
28300         return text.replace(this.entityRegExp, function(all, numeric) {
28301             if (numeric) {
28302                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28303                 // Support upper UTF
28304                 if (numeric > 65535) {
28305                     numeric -= 65536;
28306                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28307                 }
28308                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28309             }
28310             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28311         });
28312     },
28313     nativeDecode : function (text) {
28314         return text;
28315     },
28316     makeMap : function (items, delim, map) {
28317                 var i;
28318                 items = items || [];
28319                 delim = delim || ',';
28320                 if (typeof items == "string") {
28321                         items = items.split(delim);
28322                 }
28323                 map = map || {};
28324                 i = items.length;
28325                 while (i--) {
28326                         map[items[i]] = {};
28327                 }
28328                 return map;
28329         }
28330 };
28331     
28332     
28333     
28334 Roo.htmleditor.TidyEntities.init();
28335 /**
28336  * @class Roo.htmleditor.KeyEnter
28337  * Handle Enter press..
28338  * @cfg {Roo.HtmlEditorCore} core the editor.
28339  * @constructor
28340  * Create a new Filter.
28341  * @param {Object} config Configuration options
28342  */
28343
28344
28345
28346
28347
28348 Roo.htmleditor.KeyEnter = function(cfg) {
28349     Roo.apply(this, cfg);
28350     // this does not actually call walk as it's really just a abstract class
28351  
28352     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28353 }
28354
28355 //Roo.htmleditor.KeyEnter.i = 0;
28356
28357
28358 Roo.htmleditor.KeyEnter.prototype = {
28359     
28360     core : false,
28361     
28362     keypress : function(e)
28363     {
28364         if (e.charCode != 13 && e.charCode != 10) {
28365             Roo.log([e.charCode,e]);
28366             return true;
28367         }
28368         e.preventDefault();
28369         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28370         var doc = this.core.doc;
28371           //add a new line
28372        
28373     
28374         var sel = this.core.getSelection();
28375         var range = sel.getRangeAt(0);
28376         var n = range.commonAncestorContainer;
28377         var pc = range.closest([ 'ol', 'ul']);
28378         var pli = range.closest('li');
28379         if (!pc || e.ctrlKey) {
28380             // on it list, or ctrl pressed.
28381             if (!e.ctrlKey) {
28382                 sel.insertNode('br', 'after'); 
28383             } else {
28384                 // only do this if we have ctrl key..
28385                 var br = doc.createElement('br');
28386                 br.className = 'clear';
28387                 br.setAttribute('style', 'clear: both');
28388                 sel.insertNode(br, 'after'); 
28389             }
28390             
28391          
28392             this.core.undoManager.addEvent();
28393             this.core.fireEditorEvent(e);
28394             return false;
28395         }
28396         
28397         // deal with <li> insetion
28398         if (pli.innerText.trim() == '' &&
28399             pli.previousSibling &&
28400             pli.previousSibling.nodeName == 'LI' &&
28401             pli.previousSibling.innerText.trim() ==  '') {
28402             pli.parentNode.removeChild(pli.previousSibling);
28403             sel.cursorAfter(pc);
28404             this.core.undoManager.addEvent();
28405             this.core.fireEditorEvent(e);
28406             return false;
28407         }
28408     
28409         var li = doc.createElement('LI');
28410         li.innerHTML = '&nbsp;';
28411         if (!pli || !pli.firstSibling) {
28412             pc.appendChild(li);
28413         } else {
28414             pli.parentNode.insertBefore(li, pli.firstSibling);
28415         }
28416         sel.cursorText (li.firstChild);
28417       
28418         this.core.undoManager.addEvent();
28419         this.core.fireEditorEvent(e);
28420
28421         return false;
28422         
28423     
28424         
28425         
28426          
28427     }
28428 };
28429      
28430 /**
28431  * @class Roo.htmleditor.Block
28432  * Base class for html editor blocks - do not use it directly .. extend it..
28433  * @cfg {DomElement} node The node to apply stuff to.
28434  * @cfg {String} friendly_name the name that appears in the context bar about this block
28435  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28436  
28437  * @constructor
28438  * Create a new Filter.
28439  * @param {Object} config Configuration options
28440  */
28441
28442 Roo.htmleditor.Block  = function(cfg)
28443 {
28444     // do nothing .. should not be called really.
28445 }
28446 /**
28447  * factory method to get the block from an element (using cache if necessary)
28448  * @static
28449  * @param {HtmlElement} the dom element
28450  */
28451 Roo.htmleditor.Block.factory = function(node)
28452 {
28453     var cc = Roo.htmleditor.Block.cache;
28454     var id = Roo.get(node).id;
28455     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28456         Roo.htmleditor.Block.cache[id].readElement(node);
28457         return Roo.htmleditor.Block.cache[id];
28458     }
28459     var db  = node.getAttribute('data-block');
28460     if (!db) {
28461         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28462     }
28463     var cls = Roo.htmleditor['Block' + db];
28464     if (typeof(cls) == 'undefined') {
28465         //Roo.log(node.getAttribute('data-block'));
28466         Roo.log("OOps missing block : " + 'Block' + db);
28467         return false;
28468     }
28469     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28470     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
28471 };
28472
28473 /**
28474  * initalize all Elements from content that are 'blockable'
28475  * @static
28476  * @param the body element
28477  */
28478 Roo.htmleditor.Block.initAll = function(body, type)
28479 {
28480     if (typeof(type) == 'undefined') {
28481         var ia = Roo.htmleditor.Block.initAll;
28482         ia(body,'table');
28483         ia(body,'td');
28484         ia(body,'figure');
28485         return;
28486     }
28487     Roo.each(Roo.get(body).query(type), function(e) {
28488         Roo.htmleditor.Block.factory(e);    
28489     },this);
28490 };
28491 // question goes here... do we need to clear out this cache sometimes?
28492 // or show we make it relivant to the htmleditor.
28493 Roo.htmleditor.Block.cache = {};
28494
28495 Roo.htmleditor.Block.prototype = {
28496     
28497     node : false,
28498     
28499      // used by context menu
28500     friendly_name : 'Based Block',
28501     
28502     // text for button to delete this element
28503     deleteTitle : false,
28504     
28505     context : false,
28506     /**
28507      * Update a node with values from this object
28508      * @param {DomElement} node
28509      */
28510     updateElement : function(node)
28511     {
28512         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28513     },
28514      /**
28515      * convert to plain HTML for calling insertAtCursor..
28516      */
28517     toHTML : function()
28518     {
28519         return Roo.DomHelper.markup(this.toObject());
28520     },
28521     /**
28522      * used by readEleemnt to extract data from a node
28523      * may need improving as it's pretty basic
28524      
28525      * @param {DomElement} node
28526      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28527      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28528      * @param {String} style the style property - eg. text-align
28529      */
28530     getVal : function(node, tag, attr, style)
28531     {
28532         var n = node;
28533         if (tag !== true && n.tagName != tag.toUpperCase()) {
28534             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28535             // but kiss for now.
28536             n = node.getElementsByTagName(tag).item(0);
28537         }
28538         if (!n) {
28539             return '';
28540         }
28541         if (attr === false) {
28542             return n;
28543         }
28544         if (attr == 'html') {
28545             return n.innerHTML;
28546         }
28547         if (attr == 'style') {
28548             return n.style[style]; 
28549         }
28550         
28551         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
28552             
28553     },
28554     /**
28555      * create a DomHelper friendly object - for use with 
28556      * Roo.DomHelper.markup / overwrite / etc..
28557      * (override this)
28558      */
28559     toObject : function()
28560     {
28561         return {};
28562     },
28563       /**
28564      * Read a node that has a 'data-block' property - and extract the values from it.
28565      * @param {DomElement} node - the node
28566      */
28567     readElement : function(node)
28568     {
28569         
28570     } 
28571     
28572     
28573 };
28574
28575  
28576
28577 /**
28578  * @class Roo.htmleditor.BlockFigure
28579  * Block that has an image and a figcaption
28580  * @cfg {String} image_src the url for the image
28581  * @cfg {String} align (left|right) alignment for the block default left
28582  * @cfg {String} caption the text to appear below  (and in the alt tag)
28583  * @cfg {String} caption_display (block|none) display or not the caption
28584  * @cfg {String|number} image_width the width of the image number or %?
28585  * @cfg {String|number} image_height the height of the image number or %?
28586  * 
28587  * @constructor
28588  * Create a new Filter.
28589  * @param {Object} config Configuration options
28590  */
28591
28592 Roo.htmleditor.BlockFigure = function(cfg)
28593 {
28594     if (cfg.node) {
28595         this.readElement(cfg.node);
28596         this.updateElement(cfg.node);
28597     }
28598     Roo.apply(this, cfg);
28599 }
28600 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
28601  
28602     
28603     // setable values.
28604     image_src: '',
28605     align: 'center',
28606     caption : '',
28607     caption_display : 'block',
28608     width : '100%',
28609     cls : '',
28610     href: '',
28611     video_url : '',
28612     
28613     // margin: '2%', not used
28614     
28615     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
28616
28617     
28618     // used by context menu
28619     friendly_name : 'Image with caption',
28620     deleteTitle : "Delete Image and Caption",
28621     
28622     contextMenu : function(toolbar)
28623     {
28624         
28625         var block = function() {
28626             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28627         };
28628         
28629         
28630         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28631         
28632         var syncValue = toolbar.editorcore.syncValue;
28633         
28634         var fields = {};
28635         
28636         return [
28637              {
28638                 xtype : 'TextItem',
28639                 text : "Source: ",
28640                 xns : rooui.Toolbar  //Boostrap?
28641             },
28642             {
28643                 xtype : 'Button',
28644                 text: 'Change Image URL',
28645                  
28646                 listeners : {
28647                     click: function (btn, state)
28648                     {
28649                         var b = block();
28650                         
28651                         Roo.MessageBox.show({
28652                             title : "Image Source URL",
28653                             msg : "Enter the url for the image",
28654                             buttons: Roo.MessageBox.OKCANCEL,
28655                             fn: function(btn, val){
28656                                 if (btn != 'ok') {
28657                                     return;
28658                                 }
28659                                 b.image_src = val;
28660                                 b.updateElement();
28661                                 syncValue();
28662                                 toolbar.editorcore.onEditorEvent();
28663                             },
28664                             minWidth:250,
28665                             prompt:true,
28666                             //multiline: multiline,
28667                             modal : true,
28668                             value : b.image_src
28669                         });
28670                     }
28671                 },
28672                 xns : rooui.Toolbar
28673             },
28674          
28675             {
28676                 xtype : 'Button',
28677                 text: 'Change Link URL',
28678                  
28679                 listeners : {
28680                     click: function (btn, state)
28681                     {
28682                         var b = block();
28683                         
28684                         Roo.MessageBox.show({
28685                             title : "Link URL",
28686                             msg : "Enter the url for the link - leave blank to have no link",
28687                             buttons: Roo.MessageBox.OKCANCEL,
28688                             fn: function(btn, val){
28689                                 if (btn != 'ok') {
28690                                     return;
28691                                 }
28692                                 b.href = val;
28693                                 b.updateElement();
28694                                 syncValue();
28695                                 toolbar.editorcore.onEditorEvent();
28696                             },
28697                             minWidth:250,
28698                             prompt:true,
28699                             //multiline: multiline,
28700                             modal : true,
28701                             value : b.href
28702                         });
28703                     }
28704                 },
28705                 xns : rooui.Toolbar
28706             },
28707             {
28708                 xtype : 'Button',
28709                 text: 'Show Video URL',
28710                  
28711                 listeners : {
28712                     click: function (btn, state)
28713                     {
28714                         Roo.MessageBox.alert("Video URL",
28715                             block().video_url == '' ? 'This image is not linked ot a video' :
28716                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
28717                     }
28718                 },
28719                 xns : rooui.Toolbar
28720             },
28721             
28722             
28723             {
28724                 xtype : 'TextItem',
28725                 text : "Width: ",
28726                 xns : rooui.Toolbar  //Boostrap?
28727             },
28728             {
28729                 xtype : 'ComboBox',
28730                 allowBlank : false,
28731                 displayField : 'val',
28732                 editable : true,
28733                 listWidth : 100,
28734                 triggerAction : 'all',
28735                 typeAhead : true,
28736                 valueField : 'val',
28737                 width : 70,
28738                 name : 'width',
28739                 listeners : {
28740                     select : function (combo, r, index)
28741                     {
28742                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28743                         var b = block();
28744                         b.width = r.get('val');
28745                         b.updateElement();
28746                         syncValue();
28747                         toolbar.editorcore.onEditorEvent();
28748                     }
28749                 },
28750                 xns : rooui.form,
28751                 store : {
28752                     xtype : 'SimpleStore',
28753                     data : [
28754                         ['100%'],
28755                         ['80%'],
28756                         ['50%'],
28757                         ['20%'],
28758                         ['10%']
28759                     ],
28760                     fields : [ 'val'],
28761                     xns : Roo.data
28762                 }
28763             },
28764             {
28765                 xtype : 'TextItem',
28766                 text : "Align: ",
28767                 xns : rooui.Toolbar  //Boostrap?
28768             },
28769             {
28770                 xtype : 'ComboBox',
28771                 allowBlank : false,
28772                 displayField : 'val',
28773                 editable : true,
28774                 listWidth : 100,
28775                 triggerAction : 'all',
28776                 typeAhead : true,
28777                 valueField : 'val',
28778                 width : 70,
28779                 name : 'align',
28780                 listeners : {
28781                     select : function (combo, r, index)
28782                     {
28783                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28784                         var b = block();
28785                         b.align = r.get('val');
28786                         b.updateElement();
28787                         syncValue();
28788                         toolbar.editorcore.onEditorEvent();
28789                     }
28790                 },
28791                 xns : rooui.form,
28792                 store : {
28793                     xtype : 'SimpleStore',
28794                     data : [
28795                         ['left'],
28796                         ['right'],
28797                         ['center']
28798                     ],
28799                     fields : [ 'val'],
28800                     xns : Roo.data
28801                 }
28802             },
28803             
28804             
28805             {
28806                 xtype : 'Button',
28807                 text: 'Hide Caption',
28808                 name : 'caption_display',
28809                 pressed : false,
28810                 enableToggle : true,
28811                 setValue : function(v) {
28812                     // this trigger toggle.
28813                      
28814                     this.setText(v ? "Hide Caption" : "Show Caption");
28815                     this.setPressed(v != 'block');
28816                 },
28817                 listeners : {
28818                     toggle: function (btn, state)
28819                     {
28820                         var b  = block();
28821                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
28822                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
28823                         b.updateElement();
28824                         syncValue();
28825                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28826                         toolbar.editorcore.onEditorEvent();
28827                     }
28828                 },
28829                 xns : rooui.Toolbar
28830             }
28831         ];
28832         
28833     },
28834     /**
28835      * create a DomHelper friendly object - for use with
28836      * Roo.DomHelper.markup / overwrite / etc..
28837      */
28838     toObject : function()
28839     {
28840         var d = document.createElement('div');
28841         d.innerHTML = this.caption;
28842         
28843         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
28844         
28845         var iw = this.align == 'center' ? this.width : '100%';
28846         var img =   {
28847             tag : 'img',
28848             contenteditable : 'false',
28849             src : this.image_src,
28850             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
28851             style: {
28852                 width : iw,
28853                 maxWidth : iw + ' !important', // this is not getting rendered?
28854                 margin : m  
28855                 
28856             }
28857         };
28858         /*
28859         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
28860                     '<a href="{2}">' + 
28861                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
28862                     '</a>' + 
28863                 '</div>',
28864         */
28865                 
28866         if (this.href.length > 0) {
28867             img = {
28868                 tag : 'a',
28869                 href: this.href,
28870                 contenteditable : 'true',
28871                 cn : [
28872                     img
28873                 ]
28874             };
28875         }
28876         
28877         
28878         if (this.video_url.length > 0) {
28879             img = {
28880                 tag : 'div',
28881                 cls : this.cls,
28882                 frameborder : 0,
28883                 allowfullscreen : true,
28884                 width : 420,  // these are for video tricks - that we replace the outer
28885                 height : 315,
28886                 src : this.video_url,
28887                 cn : [
28888                     img
28889                 ]
28890             };
28891         }
28892         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
28893         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
28894         
28895   
28896         var ret =   {
28897             tag: 'figure',
28898             'data-block' : 'Figure',
28899             'data-width' : this.width, 
28900             contenteditable : 'false',
28901             
28902             style : {
28903                 display: 'block',
28904                 float :  this.align ,
28905                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
28906                 width : this.align == 'center' ? '100%' : this.width,
28907                 margin:  '0px',
28908                 padding: this.align == 'center' ? '0' : '0 10px' ,
28909                 textAlign : this.align   // seems to work for email..
28910                 
28911             },
28912            
28913             
28914             align : this.align,
28915             cn : [
28916                 img,
28917               
28918                 {
28919                     tag: 'figcaption',
28920                     'data-display' : this.caption_display,
28921                     style : {
28922                         textAlign : 'left',
28923                         fontSize : '16px',
28924                         lineHeight : '24px',
28925                         display : this.caption_display,
28926                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
28927                         margin: m,
28928                         width: this.align == 'center' ?  this.width : '100%' 
28929                     
28930                          
28931                     },
28932                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
28933                     cn : [
28934                         {
28935                             tag: 'div',
28936                             style  : {
28937                                 marginTop : '16px',
28938                                 textAlign : 'left'
28939                             },
28940                             align: 'left',
28941                             cn : [
28942                                 {
28943                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
28944                                     tag : 'i',
28945                                     contenteditable : true,
28946                                     html : captionhtml
28947                                 }
28948                                 
28949                             ]
28950                         }
28951                         
28952                     ]
28953                     
28954                 }
28955             ]
28956         };
28957         return ret;
28958          
28959     },
28960     
28961     readElement : function(node)
28962     {
28963         // this should not really come from the link...
28964         this.video_url = this.getVal(node, 'div', 'src');
28965         this.cls = this.getVal(node, 'div', 'class');
28966         this.href = this.getVal(node, 'a', 'href');
28967         
28968         
28969         this.image_src = this.getVal(node, 'img', 'src');
28970          
28971         this.align = this.getVal(node, 'figure', 'align');
28972         var figcaption = this.getVal(node, 'figcaption', false);
28973         if (figcaption !== '') {
28974             this.caption = this.getVal(figcaption, 'i', 'html');
28975         }
28976         
28977
28978         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
28979         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
28980         this.width = this.getVal(node, true, 'data-width');
28981         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
28982         
28983     },
28984     removeNode : function()
28985     {
28986         return this.node;
28987     }
28988     
28989   
28990    
28991      
28992     
28993     
28994     
28995     
28996 })
28997
28998  
28999
29000 /**
29001  * @class Roo.htmleditor.BlockTable
29002  * Block that manages a table
29003  * 
29004  * @constructor
29005  * Create a new Filter.
29006  * @param {Object} config Configuration options
29007  */
29008
29009 Roo.htmleditor.BlockTable = function(cfg)
29010 {
29011     if (cfg.node) {
29012         this.readElement(cfg.node);
29013         this.updateElement(cfg.node);
29014     }
29015     Roo.apply(this, cfg);
29016     if (!cfg.node) {
29017         this.rows = [];
29018         for(var r = 0; r < this.no_row; r++) {
29019             this.rows[r] = [];
29020             for(var c = 0; c < this.no_col; c++) {
29021                 this.rows[r][c] = this.emptyCell();
29022             }
29023         }
29024     }
29025     
29026     
29027 }
29028 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29029  
29030     rows : false,
29031     no_col : 1,
29032     no_row : 1,
29033     
29034     
29035     width: '100%',
29036     
29037     // used by context menu
29038     friendly_name : 'Table',
29039     deleteTitle : 'Delete Table',
29040     // context menu is drawn once..
29041     
29042     contextMenu : function(toolbar)
29043     {
29044         
29045         var block = function() {
29046             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29047         };
29048         
29049         
29050         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29051         
29052         var syncValue = toolbar.editorcore.syncValue;
29053         
29054         var fields = {};
29055         
29056         return [
29057             {
29058                 xtype : 'TextItem',
29059                 text : "Width: ",
29060                 xns : rooui.Toolbar  //Boostrap?
29061             },
29062             {
29063                 xtype : 'ComboBox',
29064                 allowBlank : false,
29065                 displayField : 'val',
29066                 editable : true,
29067                 listWidth : 100,
29068                 triggerAction : 'all',
29069                 typeAhead : true,
29070                 valueField : 'val',
29071                 width : 100,
29072                 name : 'width',
29073                 listeners : {
29074                     select : function (combo, r, index)
29075                     {
29076                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29077                         var b = block();
29078                         b.width = r.get('val');
29079                         b.updateElement();
29080                         syncValue();
29081                         toolbar.editorcore.onEditorEvent();
29082                     }
29083                 },
29084                 xns : rooui.form,
29085                 store : {
29086                     xtype : 'SimpleStore',
29087                     data : [
29088                         ['100%'],
29089                         ['auto']
29090                     ],
29091                     fields : [ 'val'],
29092                     xns : Roo.data
29093                 }
29094             },
29095             // -------- Cols
29096             
29097             {
29098                 xtype : 'TextItem',
29099                 text : "Columns: ",
29100                 xns : rooui.Toolbar  //Boostrap?
29101             },
29102          
29103             {
29104                 xtype : 'Button',
29105                 text: '-',
29106                 listeners : {
29107                     click : function (_self, e)
29108                     {
29109                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29110                         block().removeColumn();
29111                         syncValue();
29112                         toolbar.editorcore.onEditorEvent();
29113                     }
29114                 },
29115                 xns : rooui.Toolbar
29116             },
29117             {
29118                 xtype : 'Button',
29119                 text: '+',
29120                 listeners : {
29121                     click : function (_self, e)
29122                     {
29123                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29124                         block().addColumn();
29125                         syncValue();
29126                         toolbar.editorcore.onEditorEvent();
29127                     }
29128                 },
29129                 xns : rooui.Toolbar
29130             },
29131             // -------- ROWS
29132             {
29133                 xtype : 'TextItem',
29134                 text : "Rows: ",
29135                 xns : rooui.Toolbar  //Boostrap?
29136             },
29137          
29138             {
29139                 xtype : 'Button',
29140                 text: '-',
29141                 listeners : {
29142                     click : function (_self, e)
29143                     {
29144                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29145                         block().removeRow();
29146                         syncValue();
29147                         toolbar.editorcore.onEditorEvent();
29148                     }
29149                 },
29150                 xns : rooui.Toolbar
29151             },
29152             {
29153                 xtype : 'Button',
29154                 text: '+',
29155                 listeners : {
29156                     click : function (_self, e)
29157                     {
29158                         block().addRow();
29159                         syncValue();
29160                         toolbar.editorcore.onEditorEvent();
29161                     }
29162                 },
29163                 xns : rooui.Toolbar
29164             },
29165             // -------- ROWS
29166             {
29167                 xtype : 'Button',
29168                 text: 'Reset Column Widths',
29169                 listeners : {
29170                     
29171                     click : function (_self, e)
29172                     {
29173                         block().resetWidths();
29174                         syncValue();
29175                         toolbar.editorcore.onEditorEvent();
29176                     }
29177                 },
29178                 xns : rooui.Toolbar
29179             } 
29180             
29181             
29182             
29183         ];
29184         
29185     },
29186     
29187     
29188   /**
29189      * create a DomHelper friendly object - for use with
29190      * Roo.DomHelper.markup / overwrite / etc..
29191      * ?? should it be called with option to hide all editing features?
29192      */
29193     toObject : function()
29194     {
29195         
29196         var ret = {
29197             tag : 'table',
29198             contenteditable : 'false', // this stops cell selection from picking the table.
29199             'data-block' : 'Table',
29200             style : {
29201                 width:  this.width,
29202                 border : 'solid 1px #000', // ??? hard coded?
29203                 'border-collapse' : 'collapse' 
29204             },
29205             cn : [
29206                 { tag : 'tbody' , cn : [] }
29207             ]
29208         };
29209         
29210         // do we have a head = not really 
29211         var ncols = 0;
29212         Roo.each(this.rows, function( row ) {
29213             var tr = {
29214                 tag: 'tr',
29215                 style : {
29216                     margin: '6px',
29217                     border : 'solid 1px #000',
29218                     textAlign : 'left' 
29219                 },
29220                 cn : [ ]
29221             };
29222             
29223             ret.cn[0].cn.push(tr);
29224             // does the row have any properties? ?? height?
29225             var nc = 0;
29226             Roo.each(row, function( cell ) {
29227                 
29228                 var td = {
29229                     tag : 'td',
29230                     contenteditable :  'true',
29231                     'data-block' : 'Td',
29232                     html : cell.html,
29233                     style : cell.style
29234                 };
29235                 if (cell.colspan > 1) {
29236                     td.colspan = cell.colspan ;
29237                     nc += cell.colspan;
29238                 } else {
29239                     nc++;
29240                 }
29241                 if (cell.rowspan > 1) {
29242                     td.rowspan = cell.rowspan ;
29243                 }
29244                 
29245                 
29246                 // widths ?
29247                 tr.cn.push(td);
29248                     
29249                 
29250             }, this);
29251             ncols = Math.max(nc, ncols);
29252             
29253             
29254         }, this);
29255         // add the header row..
29256         
29257         ncols++;
29258          
29259         
29260         return ret;
29261          
29262     },
29263     
29264     readElement : function(node)
29265     {
29266         node  = node ? node : this.node ;
29267         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29268         
29269         this.rows = [];
29270         this.no_row = 0;
29271         var trs = Array.from(node.rows);
29272         trs.forEach(function(tr) {
29273             var row =  [];
29274             this.rows.push(row);
29275             
29276             this.no_row++;
29277             var no_column = 0;
29278             Array.from(tr.cells).forEach(function(td) {
29279                 
29280                 var add = {
29281                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29282                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29283                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29284                     html : td.innerHTML
29285                 };
29286                 no_column += add.colspan;
29287                      
29288                 
29289                 row.push(add);
29290                 
29291                 
29292             },this);
29293             this.no_col = Math.max(this.no_col, no_column);
29294             
29295             
29296         },this);
29297         
29298         
29299     },
29300     normalizeRows: function()
29301     {
29302         var ret= [];
29303         var rid = -1;
29304         this.rows.forEach(function(row) {
29305             rid++;
29306             ret[rid] = [];
29307             row = this.normalizeRow(row);
29308             var cid = 0;
29309             row.forEach(function(c) {
29310                 while (typeof(ret[rid][cid]) != 'undefined') {
29311                     cid++;
29312                 }
29313                 if (typeof(ret[rid]) == 'undefined') {
29314                     ret[rid] = [];
29315                 }
29316                 ret[rid][cid] = c;
29317                 c.row = rid;
29318                 c.col = cid;
29319                 if (c.rowspan < 2) {
29320                     return;
29321                 }
29322                 
29323                 for(var i = 1 ;i < c.rowspan; i++) {
29324                     if (typeof(ret[rid+i]) == 'undefined') {
29325                         ret[rid+i] = [];
29326                     }
29327                     ret[rid+i][cid] = c;
29328                 }
29329             });
29330         }, this);
29331         return ret;
29332     
29333     },
29334     
29335     normalizeRow: function(row)
29336     {
29337         var ret= [];
29338         row.forEach(function(c) {
29339             if (c.colspan < 2) {
29340                 ret.push(c);
29341                 return;
29342             }
29343             for(var i =0 ;i < c.colspan; i++) {
29344                 ret.push(c);
29345             }
29346         });
29347         return ret;
29348     
29349     },
29350     
29351     deleteColumn : function(sel)
29352     {
29353         if (!sel || sel.type != 'col') {
29354             return;
29355         }
29356         if (this.no_col < 2) {
29357             return;
29358         }
29359         
29360         this.rows.forEach(function(row) {
29361             var cols = this.normalizeRow(row);
29362             var col = cols[sel.col];
29363             if (col.colspan > 1) {
29364                 col.colspan --;
29365             } else {
29366                 row.remove(col);
29367             }
29368             
29369         }, this);
29370         this.no_col--;
29371         
29372     },
29373     removeColumn : function()
29374     {
29375         this.deleteColumn({
29376             type: 'col',
29377             col : this.no_col-1
29378         });
29379         this.updateElement();
29380     },
29381     
29382      
29383     addColumn : function()
29384     {
29385         
29386         this.rows.forEach(function(row) {
29387             row.push(this.emptyCell());
29388            
29389         }, this);
29390         this.updateElement();
29391     },
29392     
29393     deleteRow : function(sel)
29394     {
29395         if (!sel || sel.type != 'row') {
29396             return;
29397         }
29398         
29399         if (this.no_row < 2) {
29400             return;
29401         }
29402         
29403         var rows = this.normalizeRows();
29404         
29405         
29406         rows[sel.row].forEach(function(col) {
29407             if (col.rowspan > 1) {
29408                 col.rowspan--;
29409             } else {
29410                 col.remove = 1; // flage it as removed.
29411             }
29412             
29413         }, this);
29414         var newrows = [];
29415         this.rows.forEach(function(row) {
29416             newrow = [];
29417             row.forEach(function(c) {
29418                 if (typeof(c.remove) == 'undefined') {
29419                     newrow.push(c);
29420                 }
29421                 
29422             });
29423             if (newrow.length > 0) {
29424                 newrows.push(row);
29425             }
29426         });
29427         this.rows =  newrows;
29428         
29429         
29430         
29431         this.no_row--;
29432         this.updateElement();
29433         
29434     },
29435     removeRow : function()
29436     {
29437         this.deleteRow({
29438             type: 'row',
29439             row : this.no_row-1
29440         });
29441         
29442     },
29443     
29444      
29445     addRow : function()
29446     {
29447         
29448         var row = [];
29449         for (var i = 0; i < this.no_col; i++ ) {
29450             
29451             row.push(this.emptyCell());
29452            
29453         }
29454         this.rows.push(row);
29455         this.updateElement();
29456         
29457     },
29458      
29459     // the default cell object... at present...
29460     emptyCell : function() {
29461         return (new Roo.htmleditor.BlockTd({})).toObject();
29462         
29463      
29464     },
29465     
29466     removeNode : function()
29467     {
29468         return this.node;
29469     },
29470     
29471     
29472     
29473     resetWidths : function()
29474     {
29475         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29476             var nn = Roo.htmleditor.Block.factory(n);
29477             nn.width = '';
29478             nn.updateElement(n);
29479         });
29480     }
29481     
29482     
29483     
29484     
29485 })
29486
29487 /**
29488  *
29489  * editing a TD?
29490  *
29491  * since selections really work on the table cell, then editing really should work from there
29492  *
29493  * The original plan was to support merging etc... - but that may not be needed yet..
29494  *
29495  * So this simple version will support:
29496  *   add/remove cols
29497  *   adjust the width +/-
29498  *   reset the width...
29499  *   
29500  *
29501  */
29502
29503
29504  
29505
29506 /**
29507  * @class Roo.htmleditor.BlockTable
29508  * Block that manages a table
29509  * 
29510  * @constructor
29511  * Create a new Filter.
29512  * @param {Object} config Configuration options
29513  */
29514
29515 Roo.htmleditor.BlockTd = function(cfg)
29516 {
29517     if (cfg.node) {
29518         this.readElement(cfg.node);
29519         this.updateElement(cfg.node);
29520     }
29521     Roo.apply(this, cfg);
29522      
29523     
29524     
29525 }
29526 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29527  
29528     node : false,
29529     
29530     width: '',
29531     textAlign : 'left',
29532     valign : 'top',
29533     
29534     colspan : 1,
29535     rowspan : 1,
29536     
29537     
29538     // used by context menu
29539     friendly_name : 'Table Cell',
29540     deleteTitle : false, // use our customer delete
29541     
29542     // context menu is drawn once..
29543     
29544     contextMenu : function(toolbar)
29545     {
29546         
29547         var cell = function() {
29548             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29549         };
29550         
29551         var table = function() {
29552             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
29553         };
29554         
29555         var lr = false;
29556         var saveSel = function()
29557         {
29558             lr = toolbar.editorcore.getSelection().getRangeAt(0);
29559         }
29560         var restoreSel = function()
29561         {
29562             if (lr) {
29563                 (function() {
29564                     toolbar.editorcore.focus();
29565                     var cr = toolbar.editorcore.getSelection();
29566                     cr.removeAllRanges();
29567                     cr.addRange(lr);
29568                     toolbar.editorcore.onEditorEvent();
29569                 }).defer(10, this);
29570                 
29571                 
29572             }
29573         }
29574         
29575         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29576         
29577         var syncValue = toolbar.editorcore.syncValue;
29578         
29579         var fields = {};
29580         
29581         return [
29582             {
29583                 xtype : 'Button',
29584                 text : 'Edit Table',
29585                 listeners : {
29586                     click : function() {
29587                         var t = toolbar.tb.selectedNode.closest('table');
29588                         toolbar.editorcore.selectNode(t);
29589                         toolbar.editorcore.onEditorEvent();                        
29590                     }
29591                 }
29592                 
29593             },
29594               
29595            
29596              
29597             {
29598                 xtype : 'TextItem',
29599                 text : "Column Width: ",
29600                  xns : rooui.Toolbar 
29601                
29602             },
29603             {
29604                 xtype : 'Button',
29605                 text: '-',
29606                 listeners : {
29607                     click : function (_self, e)
29608                     {
29609                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29610                         cell().shrinkColumn();
29611                         syncValue();
29612                          toolbar.editorcore.onEditorEvent();
29613                     }
29614                 },
29615                 xns : rooui.Toolbar
29616             },
29617             {
29618                 xtype : 'Button',
29619                 text: '+',
29620                 listeners : {
29621                     click : function (_self, e)
29622                     {
29623                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29624                         cell().growColumn();
29625                         syncValue();
29626                         toolbar.editorcore.onEditorEvent();
29627                     }
29628                 },
29629                 xns : rooui.Toolbar
29630             },
29631             
29632             {
29633                 xtype : 'TextItem',
29634                 text : "Vertical Align: ",
29635                 xns : rooui.Toolbar  //Boostrap?
29636             },
29637             {
29638                 xtype : 'ComboBox',
29639                 allowBlank : false,
29640                 displayField : 'val',
29641                 editable : true,
29642                 listWidth : 100,
29643                 triggerAction : 'all',
29644                 typeAhead : true,
29645                 valueField : 'val',
29646                 width : 100,
29647                 name : 'valign',
29648                 listeners : {
29649                     select : function (combo, r, index)
29650                     {
29651                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29652                         var b = cell();
29653                         b.valign = r.get('val');
29654                         b.updateElement();
29655                         syncValue();
29656                         toolbar.editorcore.onEditorEvent();
29657                     }
29658                 },
29659                 xns : rooui.form,
29660                 store : {
29661                     xtype : 'SimpleStore',
29662                     data : [
29663                         ['top'],
29664                         ['middle'],
29665                         ['bottom'] // there are afew more... 
29666                     ],
29667                     fields : [ 'val'],
29668                     xns : Roo.data
29669                 }
29670             },
29671             
29672             {
29673                 xtype : 'TextItem',
29674                 text : "Merge Cells: ",
29675                  xns : rooui.Toolbar 
29676                
29677             },
29678             
29679             
29680             {
29681                 xtype : 'Button',
29682                 text: 'Right',
29683                 listeners : {
29684                     click : function (_self, e)
29685                     {
29686                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29687                         cell().mergeRight();
29688                         //block().growColumn();
29689                         syncValue();
29690                         toolbar.editorcore.onEditorEvent();
29691                     }
29692                 },
29693                 xns : rooui.Toolbar
29694             },
29695              
29696             {
29697                 xtype : 'Button',
29698                 text: 'Below',
29699                 listeners : {
29700                     click : function (_self, e)
29701                     {
29702                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29703                         cell().mergeBelow();
29704                         //block().growColumn();
29705                         syncValue();
29706                         toolbar.editorcore.onEditorEvent();
29707                     }
29708                 },
29709                 xns : rooui.Toolbar
29710             },
29711             {
29712                 xtype : 'TextItem',
29713                 text : "| ",
29714                  xns : rooui.Toolbar 
29715                
29716             },
29717             
29718             {
29719                 xtype : 'Button',
29720                 text: 'Split',
29721                 listeners : {
29722                     click : function (_self, e)
29723                     {
29724                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29725                         cell().split();
29726                         syncValue();
29727                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29728                         toolbar.editorcore.onEditorEvent();
29729                                              
29730                     }
29731                 },
29732                 xns : rooui.Toolbar
29733             },
29734             {
29735                 xtype : 'Fill',
29736                 xns : rooui.Toolbar 
29737                
29738             },
29739         
29740           
29741             {
29742                 xtype : 'Button',
29743                 text: 'Delete',
29744                  
29745                 xns : rooui.Toolbar,
29746                 menu : {
29747                     xtype : 'Menu',
29748                     xns : rooui.menu,
29749                     items : [
29750                         {
29751                             xtype : 'Item',
29752                             html: 'Column',
29753                             listeners : {
29754                                 click : function (_self, e)
29755                                 {
29756                                     var t = table();
29757                                     
29758                                     cell().deleteColumn();
29759                                     syncValue();
29760                                     toolbar.editorcore.selectNode(t.node);
29761                                     toolbar.editorcore.onEditorEvent();   
29762                                 }
29763                             },
29764                             xns : rooui.menu
29765                         },
29766                         {
29767                             xtype : 'Item',
29768                             html: 'Row',
29769                             listeners : {
29770                                 click : function (_self, e)
29771                                 {
29772                                     var t = table();
29773                                     cell().deleteRow();
29774                                     syncValue();
29775                                     
29776                                     toolbar.editorcore.selectNode(t.node);
29777                                     toolbar.editorcore.onEditorEvent();   
29778                                                          
29779                                 }
29780                             },
29781                             xns : rooui.menu
29782                         },
29783                        {
29784                             xtype : 'Separator',
29785                             xns : rooui.menu
29786                         },
29787                         {
29788                             xtype : 'Item',
29789                             html: 'Table',
29790                             listeners : {
29791                                 click : function (_self, e)
29792                                 {
29793                                     var t = table();
29794                                     var nn = t.node.nextSibling || t.node.previousSibling;
29795                                     t.node.parentNode.removeChild(t.node);
29796                                     if (nn) { 
29797                                         toolbar.editorcore.selectNode(nn, true);
29798                                     }
29799                                     toolbar.editorcore.onEditorEvent();   
29800                                                          
29801                                 }
29802                             },
29803                             xns : rooui.menu
29804                         }
29805                     ]
29806                 }
29807             }
29808             
29809             // align... << fixme
29810             
29811         ];
29812         
29813     },
29814     
29815     
29816   /**
29817      * create a DomHelper friendly object - for use with
29818      * Roo.DomHelper.markup / overwrite / etc..
29819      * ?? should it be called with option to hide all editing features?
29820      */
29821  /**
29822      * create a DomHelper friendly object - for use with
29823      * Roo.DomHelper.markup / overwrite / etc..
29824      * ?? should it be called with option to hide all editing features?
29825      */
29826     toObject : function()
29827     {
29828         var ret = {
29829             tag : 'td',
29830             contenteditable : 'true', // this stops cell selection from picking the table.
29831             'data-block' : 'Td',
29832             valign : this.valign,
29833             style : {  
29834                 'text-align' :  this.textAlign,
29835                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
29836                 'border-collapse' : 'collapse',
29837                 padding : '6px', // 8 for desktop / 4 for mobile
29838                 'vertical-align': this.valign
29839             },
29840             html : this.html
29841         };
29842         if (this.width != '') {
29843             ret.width = this.width;
29844             ret.style.width = this.width;
29845         }
29846         
29847         
29848         if (this.colspan > 1) {
29849             ret.colspan = this.colspan ;
29850         } 
29851         if (this.rowspan > 1) {
29852             ret.rowspan = this.rowspan ;
29853         }
29854         
29855            
29856         
29857         return ret;
29858          
29859     },
29860     
29861     readElement : function(node)
29862     {
29863         node  = node ? node : this.node ;
29864         this.width = node.style.width;
29865         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
29866         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
29867         this.html = node.innerHTML;
29868         if (node.style.textAlign != '') {
29869             this.textAlign = node.style.textAlign;
29870         }
29871         
29872         
29873     },
29874      
29875     // the default cell object... at present...
29876     emptyCell : function() {
29877         return {
29878             colspan :  1,
29879             rowspan :  1,
29880             textAlign : 'left',
29881             html : "&nbsp;" // is this going to be editable now?
29882         };
29883      
29884     },
29885     
29886     removeNode : function()
29887     {
29888         return this.node.closest('table');
29889          
29890     },
29891     
29892     cellData : false,
29893     
29894     colWidths : false,
29895     
29896     toTableArray  : function()
29897     {
29898         var ret = [];
29899         var tab = this.node.closest('tr').closest('table');
29900         Array.from(tab.rows).forEach(function(r, ri){
29901             ret[ri] = [];
29902         });
29903         var rn = 0;
29904         this.colWidths = [];
29905         var all_auto = true;
29906         Array.from(tab.rows).forEach(function(r, ri){
29907             
29908             var cn = 0;
29909             Array.from(r.cells).forEach(function(ce, ci){
29910                 var c =  {
29911                     cell : ce,
29912                     row : rn,
29913                     col: cn,
29914                     colspan : ce.colSpan,
29915                     rowspan : ce.rowSpan
29916                 };
29917                 if (ce.isEqualNode(this.node)) {
29918                     this.cellData = c;
29919                 }
29920                 // if we have been filled up by a row?
29921                 if (typeof(ret[rn][cn]) != 'undefined') {
29922                     while(typeof(ret[rn][cn]) != 'undefined') {
29923                         cn++;
29924                     }
29925                     c.col = cn;
29926                 }
29927                 
29928                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
29929                     this.colWidths[cn] =   ce.style.width;
29930                     if (this.colWidths[cn] != '') {
29931                         all_auto = false;
29932                     }
29933                 }
29934                 
29935                 
29936                 if (c.colspan < 2 && c.rowspan < 2 ) {
29937                     ret[rn][cn] = c;
29938                     cn++;
29939                     return;
29940                 }
29941                 for(var j = 0; j < c.rowspan; j++) {
29942                     if (typeof(ret[rn+j]) == 'undefined') {
29943                         continue; // we have a problem..
29944                     }
29945                     ret[rn+j][cn] = c;
29946                     for(var i = 0; i < c.colspan; i++) {
29947                         ret[rn+j][cn+i] = c;
29948                     }
29949                 }
29950                 
29951                 cn += c.colspan;
29952             }, this);
29953             rn++;
29954         }, this);
29955         
29956         // initalize widths.?
29957         // either all widths or no widths..
29958         if (all_auto) {
29959             this.colWidths[0] = false; // no widths flag.
29960         }
29961         
29962         
29963         return ret;
29964         
29965     },
29966     
29967     
29968     
29969     
29970     mergeRight: function()
29971     {
29972          
29973         // get the contents of the next cell along..
29974         var tr = this.node.closest('tr');
29975         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
29976         if (i >= tr.childNodes.length - 1) {
29977             return; // no cells on right to merge with.
29978         }
29979         var table = this.toTableArray();
29980         
29981         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
29982             return; // nothing right?
29983         }
29984         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
29985         // right cell - must be same rowspan and on the same row.
29986         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
29987             return; // right hand side is not same rowspan.
29988         }
29989         
29990         
29991         
29992         this.node.innerHTML += ' ' + rc.cell.innerHTML;
29993         tr.removeChild(rc.cell);
29994         this.colspan += rc.colspan;
29995         this.node.setAttribute('colspan', this.colspan);
29996
29997         var table = this.toTableArray();
29998         this.normalizeWidths(table);
29999         this.updateWidths(table);
30000     },
30001     
30002     
30003     mergeBelow : function()
30004     {
30005         var table = this.toTableArray();
30006         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30007             return; // no row below
30008         }
30009         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30010             return; // nothing right?
30011         }
30012         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30013         
30014         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30015             return; // right hand side is not same rowspan.
30016         }
30017         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30018         rc.cell.parentNode.removeChild(rc.cell);
30019         this.rowspan += rc.rowspan;
30020         this.node.setAttribute('rowspan', this.rowspan);
30021     },
30022     
30023     split: function()
30024     {
30025         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30026             return;
30027         }
30028         var table = this.toTableArray();
30029         var cd = this.cellData;
30030         this.rowspan = 1;
30031         this.colspan = 1;
30032         
30033         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30034              
30035             
30036             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30037                 if (r == cd.row && c == cd.col) {
30038                     this.node.removeAttribute('rowspan');
30039                     this.node.removeAttribute('colspan');
30040                 }
30041                  
30042                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30043                 ntd.removeAttribute('id'); 
30044                 ntd.style.width  = this.colWidths[c];
30045                 ntd.innerHTML = '';
30046                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30047             }
30048             
30049         }
30050         this.redrawAllCells(table);
30051         
30052     },
30053     
30054     
30055     
30056     redrawAllCells: function(table)
30057     {
30058         
30059          
30060         var tab = this.node.closest('tr').closest('table');
30061         var ctr = tab.rows[0].parentNode;
30062         Array.from(tab.rows).forEach(function(r, ri){
30063             
30064             Array.from(r.cells).forEach(function(ce, ci){
30065                 ce.parentNode.removeChild(ce);
30066             });
30067             r.parentNode.removeChild(r);
30068         });
30069         for(var r = 0 ; r < table.length; r++) {
30070             var re = tab.rows[r];
30071             
30072             var re = tab.ownerDocument.createElement('tr');
30073             ctr.appendChild(re);
30074             for(var c = 0 ; c < table[r].length; c++) {
30075                 if (table[r][c].cell === false) {
30076                     continue;
30077                 }
30078                 
30079                 re.appendChild(table[r][c].cell);
30080                  
30081                 table[r][c].cell = false;
30082             }
30083         }
30084         
30085     },
30086     updateWidths : function(table)
30087     {
30088         for(var r = 0 ; r < table.length; r++) {
30089            
30090             for(var c = 0 ; c < table[r].length; c++) {
30091                 if (table[r][c].cell === false) {
30092                     continue;
30093                 }
30094                 
30095                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30096                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30097                     el.width = Math.floor(this.colWidths[c])  +'%';
30098                     el.updateElement(el.node);
30099                 }
30100                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30101                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30102                     var width = 0;
30103                     for(var i = 0; i < table[r][c].colspan; i ++) {
30104                         width += Math.floor(this.colWidths[c + i]);
30105                     }
30106                     el.width = width  +'%';
30107                     el.updateElement(el.node);
30108                 }
30109                 table[r][c].cell = false; // done
30110             }
30111         }
30112     },
30113     normalizeWidths : function(table)
30114     {
30115         if (this.colWidths[0] === false) {
30116             var nw = 100.0 / this.colWidths.length;
30117             this.colWidths.forEach(function(w,i) {
30118                 this.colWidths[i] = nw;
30119             },this);
30120             return;
30121         }
30122     
30123         var t = 0, missing = [];
30124         
30125         this.colWidths.forEach(function(w,i) {
30126             //if you mix % and
30127             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30128             var add =  this.colWidths[i];
30129             if (add > 0) {
30130                 t+=add;
30131                 return;
30132             }
30133             missing.push(i);
30134             
30135             
30136         },this);
30137         var nc = this.colWidths.length;
30138         if (missing.length) {
30139             var mult = (nc - missing.length) / (1.0 * nc);
30140             var t = mult * t;
30141             var ew = (100 -t) / (1.0 * missing.length);
30142             this.colWidths.forEach(function(w,i) {
30143                 if (w > 0) {
30144                     this.colWidths[i] = w * mult;
30145                     return;
30146                 }
30147                 
30148                 this.colWidths[i] = ew;
30149             }, this);
30150             // have to make up numbers..
30151              
30152         }
30153         // now we should have all the widths..
30154         
30155     
30156     },
30157     
30158     shrinkColumn : function()
30159     {
30160         var table = this.toTableArray();
30161         this.normalizeWidths(table);
30162         var col = this.cellData.col;
30163         var nw = this.colWidths[col] * 0.8;
30164         if (nw < 5) {
30165             return;
30166         }
30167         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30168         this.colWidths.forEach(function(w,i) {
30169             if (i == col) {
30170                  this.colWidths[i] = nw;
30171                 return;
30172             }
30173             this.colWidths[i] += otherAdd
30174         }, this);
30175         this.updateWidths(table);
30176          
30177     },
30178     growColumn : function()
30179     {
30180         var table = this.toTableArray();
30181         this.normalizeWidths(table);
30182         var col = this.cellData.col;
30183         var nw = this.colWidths[col] * 1.2;
30184         if (nw > 90) {
30185             return;
30186         }
30187         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30188         this.colWidths.forEach(function(w,i) {
30189             if (i == col) {
30190                 this.colWidths[i] = nw;
30191                 return;
30192             }
30193             this.colWidths[i] -= otherSub
30194         }, this);
30195         this.updateWidths(table);
30196          
30197     },
30198     deleteRow : function()
30199     {
30200         // delete this rows 'tr'
30201         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30202         // then reduce the rowspan.
30203         var table = this.toTableArray();
30204         // this.cellData.row;
30205         for (var i =0;i< table[this.cellData.row].length ; i++) {
30206             var c = table[this.cellData.row][i];
30207             if (c.row != this.cellData.row) {
30208                 
30209                 c.rowspan--;
30210                 c.cell.setAttribute('rowspan', c.rowspan);
30211                 continue;
30212             }
30213             if (c.rowspan > 1) {
30214                 c.rowspan--;
30215                 c.cell.setAttribute('rowspan', c.rowspan);
30216             }
30217         }
30218         table.splice(this.cellData.row,1);
30219         this.redrawAllCells(table);
30220         
30221     },
30222     deleteColumn : function()
30223     {
30224         var table = this.toTableArray();
30225         
30226         for (var i =0;i< table.length ; i++) {
30227             var c = table[i][this.cellData.col];
30228             if (c.col != this.cellData.col) {
30229                 table[i][this.cellData.col].colspan--;
30230             } else if (c.colspan > 1) {
30231                 c.colspan--;
30232                 c.cell.setAttribute('colspan', c.colspan);
30233             }
30234             table[i].splice(this.cellData.col,1);
30235         }
30236         
30237         this.redrawAllCells(table);
30238     }
30239     
30240     
30241     
30242     
30243 })
30244
30245 //<script type="text/javascript">
30246
30247 /*
30248  * Based  Ext JS Library 1.1.1
30249  * Copyright(c) 2006-2007, Ext JS, LLC.
30250  * LGPL
30251  *
30252  */
30253  
30254 /**
30255  * @class Roo.HtmlEditorCore
30256  * @extends Roo.Component
30257  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30258  *
30259  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30260  */
30261
30262 Roo.HtmlEditorCore = function(config){
30263     
30264     
30265     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30266     
30267     
30268     this.addEvents({
30269         /**
30270          * @event initialize
30271          * Fires when the editor is fully initialized (including the iframe)
30272          * @param {Roo.HtmlEditorCore} this
30273          */
30274         initialize: true,
30275         /**
30276          * @event activate
30277          * Fires when the editor is first receives the focus. Any insertion must wait
30278          * until after this event.
30279          * @param {Roo.HtmlEditorCore} this
30280          */
30281         activate: true,
30282          /**
30283          * @event beforesync
30284          * Fires before the textarea is updated with content from the editor iframe. Return false
30285          * to cancel the sync.
30286          * @param {Roo.HtmlEditorCore} this
30287          * @param {String} html
30288          */
30289         beforesync: true,
30290          /**
30291          * @event beforepush
30292          * Fires before the iframe editor is updated with content from the textarea. Return false
30293          * to cancel the push.
30294          * @param {Roo.HtmlEditorCore} this
30295          * @param {String} html
30296          */
30297         beforepush: true,
30298          /**
30299          * @event sync
30300          * Fires when the textarea is updated with content from the editor iframe.
30301          * @param {Roo.HtmlEditorCore} this
30302          * @param {String} html
30303          */
30304         sync: true,
30305          /**
30306          * @event push
30307          * Fires when the iframe editor is updated with content from the textarea.
30308          * @param {Roo.HtmlEditorCore} this
30309          * @param {String} html
30310          */
30311         push: true,
30312         
30313         /**
30314          * @event editorevent
30315          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30316          * @param {Roo.HtmlEditorCore} this
30317          */
30318         editorevent: true 
30319          
30320         
30321     });
30322     
30323     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30324     
30325     // defaults : white / black...
30326     this.applyBlacklists();
30327     
30328     
30329     
30330 };
30331
30332
30333 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30334
30335
30336      /**
30337      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30338      */
30339     
30340     owner : false,
30341     
30342      /**
30343      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30344      *                        Roo.resizable.
30345      */
30346     resizable : false,
30347      /**
30348      * @cfg {Number} height (in pixels)
30349      */   
30350     height: 300,
30351    /**
30352      * @cfg {Number} width (in pixels)
30353      */   
30354     width: 500,
30355      /**
30356      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30357      *         if you are doing an email editor, this probably needs disabling, it's designed
30358      */
30359     autoClean: true,
30360     
30361     /**
30362      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30363      */
30364     enableBlocks : true,
30365     /**
30366      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30367      * 
30368      */
30369     stylesheets: false,
30370      /**
30371      * @cfg {String} language default en - language of text (usefull for rtl languages)
30372      * 
30373      */
30374     language: 'en',
30375     
30376     /**
30377      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30378      *          - by default they are stripped - if you are editing email you may need this.
30379      */
30380     allowComments: false,
30381     // id of frame..
30382     frameId: false,
30383     
30384     // private properties
30385     validationEvent : false,
30386     deferHeight: true,
30387     initialized : false,
30388     activated : false,
30389     sourceEditMode : false,
30390     onFocus : Roo.emptyFn,
30391     iframePad:3,
30392     hideMode:'offsets',
30393     
30394     clearUp: true,
30395     
30396     // blacklist + whitelisted elements..
30397     black: false,
30398     white: false,
30399      
30400     bodyCls : '',
30401
30402     
30403     undoManager : false,
30404     /**
30405      * Protected method that will not generally be called directly. It
30406      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30407      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30408      */
30409     getDocMarkup : function(){
30410         // body styles..
30411         var st = '';
30412         
30413         // inherit styels from page...?? 
30414         if (this.stylesheets === false) {
30415             
30416             Roo.get(document.head).select('style').each(function(node) {
30417                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30418             });
30419             
30420             Roo.get(document.head).select('link').each(function(node) { 
30421                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30422             });
30423             
30424         } else if (!this.stylesheets.length) {
30425                 // simple..
30426                 st = '<style type="text/css">' +
30427                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30428                    '</style>';
30429         } else {
30430             for (var i in this.stylesheets) {
30431                 if (typeof(this.stylesheets[i]) != 'string') {
30432                     continue;
30433                 }
30434                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30435             }
30436             
30437         }
30438         
30439         st +=  '<style type="text/css">' +
30440             'IMG { cursor: pointer } ' +
30441         '</style>';
30442         
30443         st += '<meta name="google" content="notranslate">';
30444         
30445         var cls = 'notranslate roo-htmleditor-body';
30446         
30447         if(this.bodyCls.length){
30448             cls += ' ' + this.bodyCls;
30449         }
30450         
30451         return '<html  class="notranslate" translate="no"><head>' + st  +
30452             //<style type="text/css">' +
30453             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30454             //'</style>' +
30455             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30456     },
30457
30458     // private
30459     onRender : function(ct, position)
30460     {
30461         var _t = this;
30462         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30463         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30464         
30465         
30466         this.el.dom.style.border = '0 none';
30467         this.el.dom.setAttribute('tabIndex', -1);
30468         this.el.addClass('x-hidden hide');
30469         
30470         
30471         
30472         if(Roo.isIE){ // fix IE 1px bogus margin
30473             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30474         }
30475        
30476         
30477         this.frameId = Roo.id();
30478         
30479          
30480         
30481         var iframe = this.owner.wrap.createChild({
30482             tag: 'iframe',
30483             cls: 'form-control', // bootstrap..
30484             id: this.frameId,
30485             name: this.frameId,
30486             frameBorder : 'no',
30487             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
30488         }, this.el
30489         );
30490         
30491         
30492         this.iframe = iframe.dom;
30493
30494         this.assignDocWin();
30495         
30496         this.doc.designMode = 'on';
30497        
30498         this.doc.open();
30499         this.doc.write(this.getDocMarkup());
30500         this.doc.close();
30501
30502         
30503         var task = { // must defer to wait for browser to be ready
30504             run : function(){
30505                 //console.log("run task?" + this.doc.readyState);
30506                 this.assignDocWin();
30507                 if(this.doc.body || this.doc.readyState == 'complete'){
30508                     try {
30509                         this.doc.designMode="on";
30510                         
30511                     } catch (e) {
30512                         return;
30513                     }
30514                     Roo.TaskMgr.stop(task);
30515                     this.initEditor.defer(10, this);
30516                 }
30517             },
30518             interval : 10,
30519             duration: 10000,
30520             scope: this
30521         };
30522         Roo.TaskMgr.start(task);
30523
30524     },
30525
30526     // private
30527     onResize : function(w, h)
30528     {
30529          Roo.log('resize: ' +w + ',' + h );
30530         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30531         if(!this.iframe){
30532             return;
30533         }
30534         if(typeof w == 'number'){
30535             
30536             this.iframe.style.width = w + 'px';
30537         }
30538         if(typeof h == 'number'){
30539             
30540             this.iframe.style.height = h + 'px';
30541             if(this.doc){
30542                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
30543             }
30544         }
30545         
30546     },
30547
30548     /**
30549      * Toggles the editor between standard and source edit mode.
30550      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30551      */
30552     toggleSourceEdit : function(sourceEditMode){
30553         
30554         this.sourceEditMode = sourceEditMode === true;
30555         
30556         if(this.sourceEditMode){
30557  
30558             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
30559             
30560         }else{
30561             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
30562             //this.iframe.className = '';
30563             this.deferFocus();
30564         }
30565         //this.setSize(this.owner.wrap.getSize());
30566         //this.fireEvent('editmodechange', this, this.sourceEditMode);
30567     },
30568
30569     
30570   
30571
30572     /**
30573      * Protected method that will not generally be called directly. If you need/want
30574      * custom HTML cleanup, this is the method you should override.
30575      * @param {String} html The HTML to be cleaned
30576      * return {String} The cleaned HTML
30577      */
30578     cleanHtml : function(html)
30579     {
30580         html = String(html);
30581         if(html.length > 5){
30582             if(Roo.isSafari){ // strip safari nonsense
30583                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
30584             }
30585         }
30586         if(html == '&nbsp;'){
30587             html = '';
30588         }
30589         return html;
30590     },
30591
30592     /**
30593      * HTML Editor -> Textarea
30594      * Protected method that will not generally be called directly. Syncs the contents
30595      * of the editor iframe with the textarea.
30596      */
30597     syncValue : function()
30598     {
30599         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
30600         if(this.initialized){
30601             
30602             if (this.undoManager) {
30603                 this.undoManager.addEvent();
30604             }
30605
30606             
30607             var bd = (this.doc.body || this.doc.documentElement);
30608            
30609             
30610             var sel = this.win.getSelection();
30611             
30612             var div = document.createElement('div');
30613             div.innerHTML = bd.innerHTML;
30614             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
30615             if (gtx.length > 0) {
30616                 var rm = gtx.item(0).parentNode;
30617                 rm.parentNode.removeChild(rm);
30618             }
30619             
30620            
30621             if (this.enableBlocks) {
30622                 new Roo.htmleditor.FilterBlock({ node : div });
30623             }
30624             
30625             var html = div.innerHTML;
30626             
30627             //?? tidy?
30628             if (this.autoClean) {
30629                 
30630                 new Roo.htmleditor.FilterAttributes({
30631                     node : div,
30632                     attrib_white : [
30633                             'href',
30634                             'src',
30635                             'name',
30636                             'align',
30637                             'colspan',
30638                             'rowspan',
30639                             'data-display',
30640                             'data-width',
30641                             'start' ,
30642                             'style',
30643                             // youtube embed.
30644                             'class',
30645                             'allowfullscreen',
30646                             'frameborder',
30647                             'width',
30648                             'height',
30649                             'alt'
30650                             ],
30651                     attrib_clean : ['href', 'src' ] 
30652                 });
30653                 
30654                 var tidy = new Roo.htmleditor.TidySerializer({
30655                     inner:  true
30656                 });
30657                 html  = tidy.serialize(div);
30658                 
30659             }
30660             
30661             
30662             if(Roo.isSafari){
30663                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
30664                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
30665                 if(m && m[1]){
30666                     html = '<div style="'+m[0]+'">' + html + '</div>';
30667                 }
30668             }
30669             html = this.cleanHtml(html);
30670             // fix up the special chars.. normaly like back quotes in word...
30671             // however we do not want to do this with chinese..
30672             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
30673                 
30674                 var cc = match.charCodeAt();
30675
30676                 // Get the character value, handling surrogate pairs
30677                 if (match.length == 2) {
30678                     // It's a surrogate pair, calculate the Unicode code point
30679                     var high = match.charCodeAt(0) - 0xD800;
30680                     var low  = match.charCodeAt(1) - 0xDC00;
30681                     cc = (high * 0x400) + low + 0x10000;
30682                 }  else if (
30683                     (cc >= 0x4E00 && cc < 0xA000 ) ||
30684                     (cc >= 0x3400 && cc < 0x4E00 ) ||
30685                     (cc >= 0xf900 && cc < 0xfb00 )
30686                 ) {
30687                         return match;
30688                 }  
30689          
30690                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
30691                 return "&#" + cc + ";";
30692                 
30693                 
30694             });
30695             
30696             
30697              
30698             if(this.owner.fireEvent('beforesync', this, html) !== false){
30699                 this.el.dom.value = html;
30700                 this.owner.fireEvent('sync', this, html);
30701             }
30702         }
30703     },
30704
30705     /**
30706      * TEXTAREA -> EDITABLE
30707      * Protected method that will not generally be called directly. Pushes the value of the textarea
30708      * into the iframe editor.
30709      */
30710     pushValue : function()
30711     {
30712         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
30713         if(this.initialized){
30714             var v = this.el.dom.value.trim();
30715             
30716             
30717             if(this.owner.fireEvent('beforepush', this, v) !== false){
30718                 var d = (this.doc.body || this.doc.documentElement);
30719                 d.innerHTML = v;
30720                  
30721                 this.el.dom.value = d.innerHTML;
30722                 this.owner.fireEvent('push', this, v);
30723             }
30724             if (this.autoClean) {
30725                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
30726                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
30727             }
30728             if (this.enableBlocks) {
30729                 Roo.htmleditor.Block.initAll(this.doc.body);
30730             }
30731             
30732             this.updateLanguage();
30733             
30734             var lc = this.doc.body.lastChild;
30735             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
30736                 // add an extra line at the end.
30737                 this.doc.body.appendChild(this.doc.createElement('br'));
30738             }
30739             
30740             
30741         }
30742     },
30743
30744     // private
30745     deferFocus : function(){
30746         this.focus.defer(10, this);
30747     },
30748
30749     // doc'ed in Field
30750     focus : function(){
30751         if(this.win && !this.sourceEditMode){
30752             this.win.focus();
30753         }else{
30754             this.el.focus();
30755         }
30756     },
30757     
30758     assignDocWin: function()
30759     {
30760         var iframe = this.iframe;
30761         
30762          if(Roo.isIE){
30763             this.doc = iframe.contentWindow.document;
30764             this.win = iframe.contentWindow;
30765         } else {
30766 //            if (!Roo.get(this.frameId)) {
30767 //                return;
30768 //            }
30769 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30770 //            this.win = Roo.get(this.frameId).dom.contentWindow;
30771             
30772             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
30773                 return;
30774             }
30775             
30776             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30777             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
30778         }
30779     },
30780     
30781     // private
30782     initEditor : function(){
30783         //console.log("INIT EDITOR");
30784         this.assignDocWin();
30785         
30786         
30787         
30788         this.doc.designMode="on";
30789         this.doc.open();
30790         this.doc.write(this.getDocMarkup());
30791         this.doc.close();
30792         
30793         var dbody = (this.doc.body || this.doc.documentElement);
30794         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
30795         // this copies styles from the containing element into thsi one..
30796         // not sure why we need all of this..
30797         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
30798         
30799         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
30800         //ss['background-attachment'] = 'fixed'; // w3c
30801         dbody.bgProperties = 'fixed'; // ie
30802         dbody.setAttribute("translate", "no");
30803         
30804         //Roo.DomHelper.applyStyles(dbody, ss);
30805         Roo.EventManager.on(this.doc, {
30806              
30807             'mouseup': this.onEditorEvent,
30808             'dblclick': this.onEditorEvent,
30809             'click': this.onEditorEvent,
30810             'keyup': this.onEditorEvent,
30811             
30812             buffer:100,
30813             scope: this
30814         });
30815         Roo.EventManager.on(this.doc, {
30816             'paste': this.onPasteEvent,
30817             scope : this
30818         });
30819         if(Roo.isGecko){
30820             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
30821         }
30822         //??? needed???
30823         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
30824             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
30825         }
30826         this.initialized = true;
30827
30828         
30829         // initialize special key events - enter
30830         new Roo.htmleditor.KeyEnter({core : this});
30831         
30832          
30833         
30834         this.owner.fireEvent('initialize', this);
30835         this.pushValue();
30836     },
30837     // this is to prevent a href clicks resulting in a redirect?
30838    
30839     onPasteEvent : function(e,v)
30840     {
30841         // I think we better assume paste is going to be a dirty load of rubish from word..
30842         
30843         // even pasting into a 'email version' of this widget will have to clean up that mess.
30844         var cd = (e.browserEvent.clipboardData || window.clipboardData);
30845         
30846         // check what type of paste - if it's an image, then handle it differently.
30847         if (cd.files && cd.files.length > 0) {
30848             // pasting images?
30849             var urlAPI = (window.createObjectURL && window) || 
30850                 (window.URL && URL.revokeObjectURL && URL) || 
30851                 (window.webkitURL && webkitURL);
30852     
30853             var url = urlAPI.createObjectURL( cd.files[0]);
30854             this.insertAtCursor('<img src=" + url + ">');
30855             return false;
30856         }
30857         if (cd.types.indexOf('text/html') < 0 ) {
30858             return false;
30859         }
30860         var images = [];
30861         var html = cd.getData('text/html'); // clipboard event
30862         if (cd.types.indexOf('text/rtf') > -1) {
30863             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
30864             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
30865         }
30866         //Roo.log(images);
30867         //Roo.log(imgs);
30868         // fixme..
30869         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
30870                        .map(function(g) { return g.toDataURL(); })
30871                        .filter(function(g) { return g != 'about:blank'; });
30872         
30873         //Roo.log(html);
30874         html = this.cleanWordChars(html);
30875         
30876         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
30877         
30878         
30879         var sn = this.getParentElement();
30880         // check if d contains a table, and prevent nesting??
30881         //Roo.log(d.getElementsByTagName('table'));
30882         //Roo.log(sn);
30883         //Roo.log(sn.closest('table'));
30884         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
30885             e.preventDefault();
30886             this.insertAtCursor("You can not nest tables");
30887             //Roo.log("prevent?"); // fixme - 
30888             return false;
30889         }
30890         
30891         
30892         
30893         if (images.length > 0) {
30894             // replace all v:imagedata - with img.
30895             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
30896             Roo.each(ar, function(node) {
30897                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
30898                 node.parentNode.removeChild(node);
30899             });
30900             
30901             
30902             Roo.each(d.getElementsByTagName('img'), function(img, i) {
30903                 img.setAttribute('src', images[i]);
30904             });
30905         }
30906         if (this.autoClean) {
30907             new Roo.htmleditor.FilterWord({ node : d });
30908             
30909             new Roo.htmleditor.FilterStyleToTag({ node : d });
30910             new Roo.htmleditor.FilterAttributes({
30911                 node : d,
30912                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30913                 attrib_clean : ['href', 'src' ] 
30914             });
30915             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
30916             // should be fonts..
30917             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
30918             new Roo.htmleditor.FilterParagraph({ node : d });
30919             new Roo.htmleditor.FilterSpan({ node : d });
30920             new Roo.htmleditor.FilterLongBr({ node : d });
30921             new Roo.htmleditor.FilterComment({ node : d });
30922             
30923             
30924         }
30925         if (this.enableBlocks) {
30926                 
30927             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
30928                 if (img.closest('figure')) { // assume!! that it's aready
30929                     return;
30930                 }
30931                 var fig  = new Roo.htmleditor.BlockFigure({
30932                     image_src  : img.src
30933                 });
30934                 fig.updateElement(img); // replace it..
30935                 
30936             });
30937         }
30938         
30939         
30940         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
30941         if (this.enableBlocks) {
30942             Roo.htmleditor.Block.initAll(this.doc.body);
30943         }
30944          
30945         
30946         e.preventDefault();
30947         return false;
30948         // default behaveiour should be our local cleanup paste? (optional?)
30949         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
30950         //this.owner.fireEvent('paste', e, v);
30951     },
30952     // private
30953     onDestroy : function(){
30954         
30955         
30956         
30957         if(this.rendered){
30958             
30959             //for (var i =0; i < this.toolbars.length;i++) {
30960             //    // fixme - ask toolbars for heights?
30961             //    this.toolbars[i].onDestroy();
30962            // }
30963             
30964             //this.wrap.dom.innerHTML = '';
30965             //this.wrap.remove();
30966         }
30967     },
30968
30969     // private
30970     onFirstFocus : function(){
30971         
30972         this.assignDocWin();
30973         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
30974         
30975         this.activated = true;
30976          
30977     
30978         if(Roo.isGecko){ // prevent silly gecko errors
30979             this.win.focus();
30980             var s = this.win.getSelection();
30981             if(!s.focusNode || s.focusNode.nodeType != 3){
30982                 var r = s.getRangeAt(0);
30983                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
30984                 r.collapse(true);
30985                 this.deferFocus();
30986             }
30987             try{
30988                 this.execCmd('useCSS', true);
30989                 this.execCmd('styleWithCSS', false);
30990             }catch(e){}
30991         }
30992         this.owner.fireEvent('activate', this);
30993     },
30994
30995     // private
30996     adjustFont: function(btn){
30997         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
30998         //if(Roo.isSafari){ // safari
30999         //    adjust *= 2;
31000        // }
31001         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31002         if(Roo.isSafari){ // safari
31003             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31004             v =  (v < 10) ? 10 : v;
31005             v =  (v > 48) ? 48 : v;
31006             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31007             
31008         }
31009         
31010         
31011         v = Math.max(1, v+adjust);
31012         
31013         this.execCmd('FontSize', v  );
31014     },
31015
31016     onEditorEvent : function(e)
31017     {
31018          
31019         
31020         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31021             return; // we do not handle this.. (undo manager does..)
31022         }
31023         // in theory this detects if the last element is not a br, then we try and do that.
31024         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31025         if (e &&
31026             e.target.nodeName == 'BODY' &&
31027             e.type == "mouseup" &&
31028             this.doc.body.lastChild
31029            ) {
31030             var lc = this.doc.body.lastChild;
31031             // gtx-trans is google translate plugin adding crap.
31032             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31033                 lc = lc.previousSibling;
31034             }
31035             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31036             // if last element is <BR> - then dont do anything.
31037             
31038                 var ns = this.doc.createElement('br');
31039                 this.doc.body.appendChild(ns);
31040                 range = this.doc.createRange();
31041                 range.setStartAfter(ns);
31042                 range.collapse(true);
31043                 var sel = this.win.getSelection();
31044                 sel.removeAllRanges();
31045                 sel.addRange(range);
31046             }
31047         }
31048         
31049         
31050         
31051         this.fireEditorEvent(e);
31052       //  this.updateToolbar();
31053         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31054     },
31055     
31056     fireEditorEvent: function(e)
31057     {
31058         this.owner.fireEvent('editorevent', this, e);
31059     },
31060
31061     insertTag : function(tg)
31062     {
31063         // could be a bit smarter... -> wrap the current selected tRoo..
31064         if (tg.toLowerCase() == 'span' ||
31065             tg.toLowerCase() == 'code' ||
31066             tg.toLowerCase() == 'sup' ||
31067             tg.toLowerCase() == 'sub' 
31068             ) {
31069             
31070             range = this.createRange(this.getSelection());
31071             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31072             wrappingNode.appendChild(range.extractContents());
31073             range.insertNode(wrappingNode);
31074
31075             return;
31076             
31077             
31078             
31079         }
31080         this.execCmd("formatblock",   tg);
31081         this.undoManager.addEvent(); 
31082     },
31083     
31084     insertText : function(txt)
31085     {
31086         
31087         
31088         var range = this.createRange();
31089         range.deleteContents();
31090                //alert(Sender.getAttribute('label'));
31091                
31092         range.insertNode(this.doc.createTextNode(txt));
31093         this.undoManager.addEvent();
31094     } ,
31095     
31096      
31097
31098     /**
31099      * Executes a Midas editor command on the editor document and performs necessary focus and
31100      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31101      * @param {String} cmd The Midas command
31102      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31103      */
31104     relayCmd : function(cmd, value)
31105     {
31106         
31107         switch (cmd) {
31108             case 'justifyleft':
31109             case 'justifyright':
31110             case 'justifycenter':
31111                 // if we are in a cell, then we will adjust the
31112                 var n = this.getParentElement();
31113                 var td = n.closest('td');
31114                 if (td) {
31115                     var bl = Roo.htmleditor.Block.factory(td);
31116                     bl.textAlign = cmd.replace('justify','');
31117                     bl.updateElement();
31118                     this.owner.fireEvent('editorevent', this);
31119                     return;
31120                 }
31121                 this.execCmd('styleWithCSS', true); // 
31122                 break;
31123             case 'bold':
31124             case 'italic':
31125                 // if there is no selection, then we insert, and set the curson inside it..
31126                 this.execCmd('styleWithCSS', false); 
31127                 break;
31128                 
31129         
31130             default:
31131                 break;
31132         }
31133         
31134         
31135         this.win.focus();
31136         this.execCmd(cmd, value);
31137         this.owner.fireEvent('editorevent', this);
31138         //this.updateToolbar();
31139         this.owner.deferFocus();
31140     },
31141
31142     /**
31143      * Executes a Midas editor command directly on the editor document.
31144      * For visual commands, you should use {@link #relayCmd} instead.
31145      * <b>This should only be called after the editor is initialized.</b>
31146      * @param {String} cmd The Midas command
31147      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31148      */
31149     execCmd : function(cmd, value){
31150         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31151         this.syncValue();
31152     },
31153  
31154  
31155    
31156     /**
31157      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31158      * to insert tRoo.
31159      * @param {String} text | dom node.. 
31160      */
31161     insertAtCursor : function(text)
31162     {
31163         
31164         if(!this.activated){
31165             return;
31166         }
31167          
31168         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31169             this.win.focus();
31170             
31171             
31172             // from jquery ui (MIT licenced)
31173             var range, node;
31174             var win = this.win;
31175             
31176             if (win.getSelection && win.getSelection().getRangeAt) {
31177                 
31178                 // delete the existing?
31179                 
31180                 this.createRange(this.getSelection()).deleteContents();
31181                 range = win.getSelection().getRangeAt(0);
31182                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31183                 range.insertNode(node);
31184                 range = range.cloneRange();
31185                 range.collapse(false);
31186                  
31187                 win.getSelection().removeAllRanges();
31188                 win.getSelection().addRange(range);
31189                 
31190                 
31191                 
31192             } else if (win.document.selection && win.document.selection.createRange) {
31193                 // no firefox support
31194                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31195                 win.document.selection.createRange().pasteHTML(txt);
31196             
31197             } else {
31198                 // no firefox support
31199                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31200                 this.execCmd('InsertHTML', txt);
31201             } 
31202             this.syncValue();
31203             
31204             this.deferFocus();
31205         }
31206     },
31207  // private
31208     mozKeyPress : function(e){
31209         if(e.ctrlKey){
31210             var c = e.getCharCode(), cmd;
31211           
31212             if(c > 0){
31213                 c = String.fromCharCode(c).toLowerCase();
31214                 switch(c){
31215                     case 'b':
31216                         cmd = 'bold';
31217                         break;
31218                     case 'i':
31219                         cmd = 'italic';
31220                         break;
31221                     
31222                     case 'u':
31223                         cmd = 'underline';
31224                         break;
31225                     
31226                     //case 'v':
31227                       //  this.cleanUpPaste.defer(100, this);
31228                       //  return;
31229                         
31230                 }
31231                 if(cmd){
31232                     
31233                     this.relayCmd(cmd);
31234                     //this.win.focus();
31235                     //this.execCmd(cmd);
31236                     //this.deferFocus();
31237                     e.preventDefault();
31238                 }
31239                 
31240             }
31241         }
31242     },
31243
31244     // private
31245     fixKeys : function(){ // load time branching for fastest keydown performance
31246         
31247         
31248         if(Roo.isIE){
31249             return function(e){
31250                 var k = e.getKey(), r;
31251                 if(k == e.TAB){
31252                     e.stopEvent();
31253                     r = this.doc.selection.createRange();
31254                     if(r){
31255                         r.collapse(true);
31256                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31257                         this.deferFocus();
31258                     }
31259                     return;
31260                 }
31261                 /// this is handled by Roo.htmleditor.KeyEnter
31262                  /*
31263                 if(k == e.ENTER){
31264                     r = this.doc.selection.createRange();
31265                     if(r){
31266                         var target = r.parentElement();
31267                         if(!target || target.tagName.toLowerCase() != 'li'){
31268                             e.stopEvent();
31269                             r.pasteHTML('<br/>');
31270                             r.collapse(false);
31271                             r.select();
31272                         }
31273                     }
31274                 }
31275                 */
31276                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31277                 //    this.cleanUpPaste.defer(100, this);
31278                 //    return;
31279                 //}
31280                 
31281                 
31282             };
31283         }else if(Roo.isOpera){
31284             return function(e){
31285                 var k = e.getKey();
31286                 if(k == e.TAB){
31287                     e.stopEvent();
31288                     this.win.focus();
31289                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31290                     this.deferFocus();
31291                 }
31292                
31293                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31294                 //    this.cleanUpPaste.defer(100, this);
31295                  //   return;
31296                 //}
31297                 
31298             };
31299         }else if(Roo.isSafari){
31300             return function(e){
31301                 var k = e.getKey();
31302                 
31303                 if(k == e.TAB){
31304                     e.stopEvent();
31305                     this.execCmd('InsertText','\t');
31306                     this.deferFocus();
31307                     return;
31308                 }
31309                  this.mozKeyPress(e);
31310                 
31311                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31312                  //   this.cleanUpPaste.defer(100, this);
31313                  //   return;
31314                // }
31315                 
31316              };
31317         }
31318     }(),
31319     
31320     getAllAncestors: function()
31321     {
31322         var p = this.getSelectedNode();
31323         var a = [];
31324         if (!p) {
31325             a.push(p); // push blank onto stack..
31326             p = this.getParentElement();
31327         }
31328         
31329         
31330         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31331             a.push(p);
31332             p = p.parentNode;
31333         }
31334         a.push(this.doc.body);
31335         return a;
31336     },
31337     lastSel : false,
31338     lastSelNode : false,
31339     
31340     
31341     getSelection : function() 
31342     {
31343         this.assignDocWin();
31344         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31345     },
31346     /**
31347      * Select a dom node
31348      * @param {DomElement} node the node to select
31349      */
31350     selectNode : function(node, collapse)
31351     {
31352         var nodeRange = node.ownerDocument.createRange();
31353         try {
31354             nodeRange.selectNode(node);
31355         } catch (e) {
31356             nodeRange.selectNodeContents(node);
31357         }
31358         if (collapse === true) {
31359             nodeRange.collapse(true);
31360         }
31361         //
31362         var s = this.win.getSelection();
31363         s.removeAllRanges();
31364         s.addRange(nodeRange);
31365     },
31366     
31367     getSelectedNode: function() 
31368     {
31369         // this may only work on Gecko!!!
31370         
31371         // should we cache this!!!!
31372         
31373          
31374          
31375         var range = this.createRange(this.getSelection()).cloneRange();
31376         
31377         if (Roo.isIE) {
31378             var parent = range.parentElement();
31379             while (true) {
31380                 var testRange = range.duplicate();
31381                 testRange.moveToElementText(parent);
31382                 if (testRange.inRange(range)) {
31383                     break;
31384                 }
31385                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31386                     break;
31387                 }
31388                 parent = parent.parentElement;
31389             }
31390             return parent;
31391         }
31392         
31393         // is ancestor a text element.
31394         var ac =  range.commonAncestorContainer;
31395         if (ac.nodeType == 3) {
31396             ac = ac.parentNode;
31397         }
31398         
31399         var ar = ac.childNodes;
31400          
31401         var nodes = [];
31402         var other_nodes = [];
31403         var has_other_nodes = false;
31404         for (var i=0;i<ar.length;i++) {
31405             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31406                 continue;
31407             }
31408             // fullly contained node.
31409             
31410             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31411                 nodes.push(ar[i]);
31412                 continue;
31413             }
31414             
31415             // probably selected..
31416             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31417                 other_nodes.push(ar[i]);
31418                 continue;
31419             }
31420             // outer..
31421             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31422                 continue;
31423             }
31424             
31425             
31426             has_other_nodes = true;
31427         }
31428         if (!nodes.length && other_nodes.length) {
31429             nodes= other_nodes;
31430         }
31431         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31432             return false;
31433         }
31434         
31435         return nodes[0];
31436     },
31437     
31438     
31439     createRange: function(sel)
31440     {
31441         // this has strange effects when using with 
31442         // top toolbar - not sure if it's a great idea.
31443         //this.editor.contentWindow.focus();
31444         if (typeof sel != "undefined") {
31445             try {
31446                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31447             } catch(e) {
31448                 return this.doc.createRange();
31449             }
31450         } else {
31451             return this.doc.createRange();
31452         }
31453     },
31454     getParentElement: function()
31455     {
31456         
31457         this.assignDocWin();
31458         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31459         
31460         var range = this.createRange(sel);
31461          
31462         try {
31463             var p = range.commonAncestorContainer;
31464             while (p.nodeType == 3) { // text node
31465                 p = p.parentNode;
31466             }
31467             return p;
31468         } catch (e) {
31469             return null;
31470         }
31471     
31472     },
31473     /***
31474      *
31475      * Range intersection.. the hard stuff...
31476      *  '-1' = before
31477      *  '0' = hits..
31478      *  '1' = after.
31479      *         [ -- selected range --- ]
31480      *   [fail]                        [fail]
31481      *
31482      *    basically..
31483      *      if end is before start or  hits it. fail.
31484      *      if start is after end or hits it fail.
31485      *
31486      *   if either hits (but other is outside. - then it's not 
31487      *   
31488      *    
31489      **/
31490     
31491     
31492     // @see http://www.thismuchiknow.co.uk/?p=64.
31493     rangeIntersectsNode : function(range, node)
31494     {
31495         var nodeRange = node.ownerDocument.createRange();
31496         try {
31497             nodeRange.selectNode(node);
31498         } catch (e) {
31499             nodeRange.selectNodeContents(node);
31500         }
31501     
31502         var rangeStartRange = range.cloneRange();
31503         rangeStartRange.collapse(true);
31504     
31505         var rangeEndRange = range.cloneRange();
31506         rangeEndRange.collapse(false);
31507     
31508         var nodeStartRange = nodeRange.cloneRange();
31509         nodeStartRange.collapse(true);
31510     
31511         var nodeEndRange = nodeRange.cloneRange();
31512         nodeEndRange.collapse(false);
31513     
31514         return rangeStartRange.compareBoundaryPoints(
31515                  Range.START_TO_START, nodeEndRange) == -1 &&
31516                rangeEndRange.compareBoundaryPoints(
31517                  Range.START_TO_START, nodeStartRange) == 1;
31518         
31519          
31520     },
31521     rangeCompareNode : function(range, node)
31522     {
31523         var nodeRange = node.ownerDocument.createRange();
31524         try {
31525             nodeRange.selectNode(node);
31526         } catch (e) {
31527             nodeRange.selectNodeContents(node);
31528         }
31529         
31530         
31531         range.collapse(true);
31532     
31533         nodeRange.collapse(true);
31534      
31535         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31536         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
31537          
31538         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31539         
31540         var nodeIsBefore   =  ss == 1;
31541         var nodeIsAfter    = ee == -1;
31542         
31543         if (nodeIsBefore && nodeIsAfter) {
31544             return 0; // outer
31545         }
31546         if (!nodeIsBefore && nodeIsAfter) {
31547             return 1; //right trailed.
31548         }
31549         
31550         if (nodeIsBefore && !nodeIsAfter) {
31551             return 2;  // left trailed.
31552         }
31553         // fully contined.
31554         return 3;
31555     },
31556  
31557     cleanWordChars : function(input) {// change the chars to hex code
31558         
31559        var swapCodes  = [ 
31560             [    8211, "&#8211;" ], 
31561             [    8212, "&#8212;" ], 
31562             [    8216,  "'" ],  
31563             [    8217, "'" ],  
31564             [    8220, '"' ],  
31565             [    8221, '"' ],  
31566             [    8226, "*" ],  
31567             [    8230, "..." ]
31568         ]; 
31569         var output = input;
31570         Roo.each(swapCodes, function(sw) { 
31571             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
31572             
31573             output = output.replace(swapper, sw[1]);
31574         });
31575         
31576         return output;
31577     },
31578     
31579      
31580     
31581         
31582     
31583     cleanUpChild : function (node)
31584     {
31585         
31586         new Roo.htmleditor.FilterComment({node : node});
31587         new Roo.htmleditor.FilterAttributes({
31588                 node : node,
31589                 attrib_black : this.ablack,
31590                 attrib_clean : this.aclean,
31591                 style_white : this.cwhite,
31592                 style_black : this.cblack
31593         });
31594         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
31595         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
31596          
31597         
31598     },
31599     
31600     /**
31601      * Clean up MS wordisms...
31602      * @deprecated - use filter directly
31603      */
31604     cleanWord : function(node)
31605     {
31606         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
31607         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
31608         
31609     },
31610    
31611     
31612     /**
31613
31614      * @deprecated - use filters
31615      */
31616     cleanTableWidths : function(node)
31617     {
31618         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
31619         
31620  
31621     },
31622     
31623      
31624         
31625     applyBlacklists : function()
31626     {
31627         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
31628         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
31629         
31630         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
31631         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
31632         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
31633         
31634         this.white = [];
31635         this.black = [];
31636         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
31637             if (b.indexOf(tag) > -1) {
31638                 return;
31639             }
31640             this.white.push(tag);
31641             
31642         }, this);
31643         
31644         Roo.each(w, function(tag) {
31645             if (b.indexOf(tag) > -1) {
31646                 return;
31647             }
31648             if (this.white.indexOf(tag) > -1) {
31649                 return;
31650             }
31651             this.white.push(tag);
31652             
31653         }, this);
31654         
31655         
31656         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
31657             if (w.indexOf(tag) > -1) {
31658                 return;
31659             }
31660             this.black.push(tag);
31661             
31662         }, this);
31663         
31664         Roo.each(b, function(tag) {
31665             if (w.indexOf(tag) > -1) {
31666                 return;
31667             }
31668             if (this.black.indexOf(tag) > -1) {
31669                 return;
31670             }
31671             this.black.push(tag);
31672             
31673         }, this);
31674         
31675         
31676         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
31677         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
31678         
31679         this.cwhite = [];
31680         this.cblack = [];
31681         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
31682             if (b.indexOf(tag) > -1) {
31683                 return;
31684             }
31685             this.cwhite.push(tag);
31686             
31687         }, this);
31688         
31689         Roo.each(w, function(tag) {
31690             if (b.indexOf(tag) > -1) {
31691                 return;
31692             }
31693             if (this.cwhite.indexOf(tag) > -1) {
31694                 return;
31695             }
31696             this.cwhite.push(tag);
31697             
31698         }, this);
31699         
31700         
31701         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
31702             if (w.indexOf(tag) > -1) {
31703                 return;
31704             }
31705             this.cblack.push(tag);
31706             
31707         }, this);
31708         
31709         Roo.each(b, function(tag) {
31710             if (w.indexOf(tag) > -1) {
31711                 return;
31712             }
31713             if (this.cblack.indexOf(tag) > -1) {
31714                 return;
31715             }
31716             this.cblack.push(tag);
31717             
31718         }, this);
31719     },
31720     
31721     setStylesheets : function(stylesheets)
31722     {
31723         if(typeof(stylesheets) == 'string'){
31724             Roo.get(this.iframe.contentDocument.head).createChild({
31725                 tag : 'link',
31726                 rel : 'stylesheet',
31727                 type : 'text/css',
31728                 href : stylesheets
31729             });
31730             
31731             return;
31732         }
31733         var _this = this;
31734      
31735         Roo.each(stylesheets, function(s) {
31736             if(!s.length){
31737                 return;
31738             }
31739             
31740             Roo.get(_this.iframe.contentDocument.head).createChild({
31741                 tag : 'link',
31742                 rel : 'stylesheet',
31743                 type : 'text/css',
31744                 href : s
31745             });
31746         });
31747
31748         
31749     },
31750     
31751     
31752     updateLanguage : function()
31753     {
31754         if (!this.iframe || !this.iframe.contentDocument) {
31755             return;
31756         }
31757         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
31758     },
31759     
31760     
31761     removeStylesheets : function()
31762     {
31763         var _this = this;
31764         
31765         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
31766             s.remove();
31767         });
31768     },
31769     
31770     setStyle : function(style)
31771     {
31772         Roo.get(this.iframe.contentDocument.head).createChild({
31773             tag : 'style',
31774             type : 'text/css',
31775             html : style
31776         });
31777
31778         return;
31779     }
31780     
31781     // hide stuff that is not compatible
31782     /**
31783      * @event blur
31784      * @hide
31785      */
31786     /**
31787      * @event change
31788      * @hide
31789      */
31790     /**
31791      * @event focus
31792      * @hide
31793      */
31794     /**
31795      * @event specialkey
31796      * @hide
31797      */
31798     /**
31799      * @cfg {String} fieldClass @hide
31800      */
31801     /**
31802      * @cfg {String} focusClass @hide
31803      */
31804     /**
31805      * @cfg {String} autoCreate @hide
31806      */
31807     /**
31808      * @cfg {String} inputType @hide
31809      */
31810     /**
31811      * @cfg {String} invalidClass @hide
31812      */
31813     /**
31814      * @cfg {String} invalidText @hide
31815      */
31816     /**
31817      * @cfg {String} msgFx @hide
31818      */
31819     /**
31820      * @cfg {String} validateOnBlur @hide
31821      */
31822 });
31823
31824 Roo.HtmlEditorCore.white = [
31825         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
31826         
31827        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
31828        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
31829        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
31830        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
31831        'TABLE',   'UL',         'XMP', 
31832        
31833        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
31834       'THEAD',   'TR', 
31835      
31836       'DIR', 'MENU', 'OL', 'UL', 'DL',
31837        
31838       'EMBED',  'OBJECT'
31839 ];
31840
31841
31842 Roo.HtmlEditorCore.black = [
31843     //    'embed',  'object', // enable - backend responsiblity to clean thiese
31844         'APPLET', // 
31845         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
31846         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
31847         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
31848         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
31849         //'FONT' // CLEAN LATER..
31850         'COLGROUP', 'COL'   // messy tables.
31851         
31852         
31853 ];
31854 Roo.HtmlEditorCore.clean = [ // ?? needed???
31855      'SCRIPT', 'STYLE', 'TITLE', 'XML'
31856 ];
31857 Roo.HtmlEditorCore.tag_remove = [
31858     'FONT', 'TBODY'  
31859 ];
31860 // attributes..
31861
31862 Roo.HtmlEditorCore.ablack = [
31863     'on'
31864 ];
31865     
31866 Roo.HtmlEditorCore.aclean = [ 
31867     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
31868 ];
31869
31870 // protocols..
31871 Roo.HtmlEditorCore.pwhite= [
31872         'http',  'https',  'mailto'
31873 ];
31874
31875 // white listed style attributes.
31876 Roo.HtmlEditorCore.cwhite= [
31877       //  'text-align', /// default is to allow most things..
31878       
31879          
31880 //        'font-size'//??
31881 ];
31882
31883 // black listed style attributes.
31884 Roo.HtmlEditorCore.cblack= [
31885       //  'font-size' -- this can be set by the project 
31886 ];
31887
31888
31889
31890
31891     /*
31892  * - LGPL
31893  *
31894  * HtmlEditor
31895  * 
31896  */
31897
31898 /**
31899  * @class Roo.bootstrap.form.HtmlEditor
31900  * @extends Roo.bootstrap.form.TextArea
31901  * Bootstrap HtmlEditor class
31902
31903  * @constructor
31904  * Create a new HtmlEditor
31905  * @param {Object} config The config object
31906  */
31907
31908 Roo.bootstrap.form.HtmlEditor = function(config){
31909     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
31910     if (!this.toolbars) {
31911         this.toolbars = [];
31912     }
31913     
31914     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
31915     this.addEvents({
31916             /**
31917              * @event initialize
31918              * Fires when the editor is fully initialized (including the iframe)
31919              * @param {HtmlEditor} this
31920              */
31921             initialize: true,
31922             /**
31923              * @event activate
31924              * Fires when the editor is first receives the focus. Any insertion must wait
31925              * until after this event.
31926              * @param {HtmlEditor} this
31927              */
31928             activate: true,
31929              /**
31930              * @event beforesync
31931              * Fires before the textarea is updated with content from the editor iframe. Return false
31932              * to cancel the sync.
31933              * @param {HtmlEditor} this
31934              * @param {String} html
31935              */
31936             beforesync: true,
31937              /**
31938              * @event beforepush
31939              * Fires before the iframe editor is updated with content from the textarea. Return false
31940              * to cancel the push.
31941              * @param {HtmlEditor} this
31942              * @param {String} html
31943              */
31944             beforepush: true,
31945              /**
31946              * @event sync
31947              * Fires when the textarea is updated with content from the editor iframe.
31948              * @param {HtmlEditor} this
31949              * @param {String} html
31950              */
31951             sync: true,
31952              /**
31953              * @event push
31954              * Fires when the iframe editor is updated with content from the textarea.
31955              * @param {HtmlEditor} this
31956              * @param {String} html
31957              */
31958             push: true,
31959              /**
31960              * @event editmodechange
31961              * Fires when the editor switches edit modes
31962              * @param {HtmlEditor} this
31963              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
31964              */
31965             editmodechange: true,
31966             /**
31967              * @event editorevent
31968              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31969              * @param {HtmlEditor} this
31970              */
31971             editorevent: true,
31972             /**
31973              * @event firstfocus
31974              * Fires when on first focus - needed by toolbars..
31975              * @param {HtmlEditor} this
31976              */
31977             firstfocus: true,
31978             /**
31979              * @event autosave
31980              * Auto save the htmlEditor value as a file into Events
31981              * @param {HtmlEditor} this
31982              */
31983             autosave: true,
31984             /**
31985              * @event savedpreview
31986              * preview the saved version of htmlEditor
31987              * @param {HtmlEditor} this
31988              */
31989             savedpreview: true
31990         });
31991 };
31992
31993
31994 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
31995     
31996     
31997       /**
31998      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
31999      */
32000     toolbars : false,
32001     
32002      /**
32003     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32004     */
32005     btns : [],
32006    
32007      /**
32008      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
32009      *                        Roo.resizable.
32010      */
32011     resizable : false,
32012      /**
32013      * @cfg {Number} height (in pixels)
32014      */   
32015     height: 300,
32016    /**
32017      * @cfg {Number} width (in pixels)
32018      */   
32019     width: false,
32020     
32021     /**
32022      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32023      * 
32024      */
32025     stylesheets: false,
32026     
32027     // id of frame..
32028     frameId: false,
32029     
32030     // private properties
32031     validationEvent : false,
32032     deferHeight: true,
32033     initialized : false,
32034     activated : false,
32035     
32036     onFocus : Roo.emptyFn,
32037     iframePad:3,
32038     hideMode:'offsets',
32039     
32040     tbContainer : false,
32041     
32042     bodyCls : '',
32043     
32044     toolbarContainer :function() {
32045         return this.wrap.select('.x-html-editor-tb',true).first();
32046     },
32047
32048     /**
32049      * Protected method that will not generally be called directly. It
32050      * is called when the editor creates its toolbar. Override this method if you need to
32051      * add custom toolbar buttons.
32052      * @param {HtmlEditor} editor
32053      */
32054     createToolbar : function(){
32055         Roo.log('renewing');
32056         Roo.log("create toolbars");
32057         
32058         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32059         this.toolbars[0].render(this.toolbarContainer());
32060         
32061         return;
32062         
32063 //        if (!editor.toolbars || !editor.toolbars.length) {
32064 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32065 //        }
32066 //        
32067 //        for (var i =0 ; i < editor.toolbars.length;i++) {
32068 //            editor.toolbars[i] = Roo.factory(
32069 //                    typeof(editor.toolbars[i]) == 'string' ?
32070 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
32071 //                Roo.bootstrap.form.HtmlEditor);
32072 //            editor.toolbars[i].init(editor);
32073 //        }
32074     },
32075
32076      
32077     // private
32078     onRender : function(ct, position)
32079     {
32080        // Roo.log("Call onRender: " + this.xtype);
32081         var _t = this;
32082         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32083       
32084         this.wrap = this.inputEl().wrap({
32085             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32086         });
32087         
32088         this.editorcore.onRender(ct, position);
32089          
32090         if (this.resizable) {
32091             this.resizeEl = new Roo.Resizable(this.wrap, {
32092                 pinned : true,
32093                 wrap: true,
32094                 dynamic : true,
32095                 minHeight : this.height,
32096                 height: this.height,
32097                 handles : this.resizable,
32098                 width: this.width,
32099                 listeners : {
32100                     resize : function(r, w, h) {
32101                         _t.onResize(w,h); // -something
32102                     }
32103                 }
32104             });
32105             
32106         }
32107         this.createToolbar(this);
32108        
32109         
32110         if(!this.width && this.resizable){
32111             this.setSize(this.wrap.getSize());
32112         }
32113         if (this.resizeEl) {
32114             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32115             // should trigger onReize..
32116         }
32117         
32118     },
32119
32120     // private
32121     onResize : function(w, h)
32122     {
32123         Roo.log('resize: ' +w + ',' + h );
32124         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32125         var ew = false;
32126         var eh = false;
32127         
32128         if(this.inputEl() ){
32129             if(typeof w == 'number'){
32130                 var aw = w - this.wrap.getFrameWidth('lr');
32131                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32132                 ew = aw;
32133             }
32134             if(typeof h == 'number'){
32135                  var tbh = -11;  // fixme it needs to tool bar size!
32136                 for (var i =0; i < this.toolbars.length;i++) {
32137                     // fixme - ask toolbars for heights?
32138                     tbh += this.toolbars[i].el.getHeight();
32139                     //if (this.toolbars[i].footer) {
32140                     //    tbh += this.toolbars[i].footer.el.getHeight();
32141                     //}
32142                 }
32143               
32144                 
32145                 
32146                 
32147                 
32148                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32149                 ah -= 5; // knock a few pixes off for look..
32150                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32151                 var eh = ah;
32152             }
32153         }
32154         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32155         this.editorcore.onResize(ew,eh);
32156         
32157     },
32158
32159     /**
32160      * Toggles the editor between standard and source edit mode.
32161      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32162      */
32163     toggleSourceEdit : function(sourceEditMode)
32164     {
32165         this.editorcore.toggleSourceEdit(sourceEditMode);
32166         
32167         if(this.editorcore.sourceEditMode){
32168             Roo.log('editor - showing textarea');
32169             
32170 //            Roo.log('in');
32171 //            Roo.log(this.syncValue());
32172             this.syncValue();
32173             this.inputEl().removeClass(['hide', 'x-hidden']);
32174             this.inputEl().dom.removeAttribute('tabIndex');
32175             this.inputEl().focus();
32176         }else{
32177             Roo.log('editor - hiding textarea');
32178 //            Roo.log('out')
32179 //            Roo.log(this.pushValue()); 
32180             this.pushValue();
32181             
32182             this.inputEl().addClass(['hide', 'x-hidden']);
32183             this.inputEl().dom.setAttribute('tabIndex', -1);
32184             //this.deferFocus();
32185         }
32186          
32187         if(this.resizable){
32188             this.setSize(this.wrap.getSize());
32189         }
32190         
32191         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32192     },
32193  
32194     // private (for BoxComponent)
32195     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32196
32197     // private (for BoxComponent)
32198     getResizeEl : function(){
32199         return this.wrap;
32200     },
32201
32202     // private (for BoxComponent)
32203     getPositionEl : function(){
32204         return this.wrap;
32205     },
32206
32207     // private
32208     initEvents : function(){
32209         this.originalValue = this.getValue();
32210     },
32211
32212 //    /**
32213 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32214 //     * @method
32215 //     */
32216 //    markInvalid : Roo.emptyFn,
32217 //    /**
32218 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32219 //     * @method
32220 //     */
32221 //    clearInvalid : Roo.emptyFn,
32222
32223     setValue : function(v){
32224         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32225         this.editorcore.pushValue();
32226     },
32227
32228      
32229     // private
32230     deferFocus : function(){
32231         this.focus.defer(10, this);
32232     },
32233
32234     // doc'ed in Field
32235     focus : function(){
32236         this.editorcore.focus();
32237         
32238     },
32239       
32240
32241     // private
32242     onDestroy : function(){
32243         
32244         
32245         
32246         if(this.rendered){
32247             
32248             for (var i =0; i < this.toolbars.length;i++) {
32249                 // fixme - ask toolbars for heights?
32250                 this.toolbars[i].onDestroy();
32251             }
32252             
32253             this.wrap.dom.innerHTML = '';
32254             this.wrap.remove();
32255         }
32256     },
32257
32258     // private
32259     onFirstFocus : function(){
32260         //Roo.log("onFirstFocus");
32261         this.editorcore.onFirstFocus();
32262          for (var i =0; i < this.toolbars.length;i++) {
32263             this.toolbars[i].onFirstFocus();
32264         }
32265         
32266     },
32267     
32268     // private
32269     syncValue : function()
32270     {   
32271         this.editorcore.syncValue();
32272     },
32273     
32274     pushValue : function()
32275     {   
32276         this.editorcore.pushValue();
32277     }
32278      
32279     
32280     // hide stuff that is not compatible
32281     /**
32282      * @event blur
32283      * @hide
32284      */
32285     /**
32286      * @event change
32287      * @hide
32288      */
32289     /**
32290      * @event focus
32291      * @hide
32292      */
32293     /**
32294      * @event specialkey
32295      * @hide
32296      */
32297     /**
32298      * @cfg {String} fieldClass @hide
32299      */
32300     /**
32301      * @cfg {String} focusClass @hide
32302      */
32303     /**
32304      * @cfg {String} autoCreate @hide
32305      */
32306     /**
32307      * @cfg {String} inputType @hide
32308      */
32309      
32310     /**
32311      * @cfg {String} invalidText @hide
32312      */
32313     /**
32314      * @cfg {String} msgFx @hide
32315      */
32316     /**
32317      * @cfg {String} validateOnBlur @hide
32318      */
32319 });
32320  
32321     
32322    
32323    
32324    
32325       
32326 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32327 /**
32328  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32329  * @parent Roo.bootstrap.form.HtmlEditor
32330  * @extends Roo.bootstrap.nav.Simplebar
32331  * Basic Toolbar
32332  * 
32333  * @example
32334  * Usage:
32335  *
32336  new Roo.bootstrap.form.HtmlEditor({
32337     ....
32338     toolbars : [
32339         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32340             disable : { fonts: 1 , format: 1, ..., ... , ...],
32341             btns : [ .... ]
32342         })
32343     }
32344      
32345  * 
32346  * @cfg {Object} disable List of elements to disable..
32347  * @cfg {Array} btns List of additional buttons.
32348  * 
32349  * 
32350  * NEEDS Extra CSS? 
32351  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32352  */
32353  
32354 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32355 {
32356     
32357     Roo.apply(this, config);
32358     
32359     // default disabled, based on 'good practice'..
32360     this.disable = this.disable || {};
32361     Roo.applyIf(this.disable, {
32362         fontSize : true,
32363         colors : true,
32364         specialElements : true
32365     });
32366     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32367     
32368     this.editor = config.editor;
32369     this.editorcore = config.editor.editorcore;
32370     
32371     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32372     
32373     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32374     // dont call parent... till later.
32375 }
32376 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32377      
32378     bar : true,
32379     
32380     editor : false,
32381     editorcore : false,
32382     
32383     
32384     formats : [
32385         "p" ,  
32386         "h1","h2","h3","h4","h5","h6", 
32387         "pre", "code", 
32388         "abbr", "acronym", "address", "cite", "samp", "var",
32389         'div','span'
32390     ],
32391     
32392     onRender : function(ct, position)
32393     {
32394        // Roo.log("Call onRender: " + this.xtype);
32395         
32396        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32397        Roo.log(this.el);
32398        this.el.dom.style.marginBottom = '0';
32399        var _this = this;
32400        var editorcore = this.editorcore;
32401        var editor= this.editor;
32402        
32403        var children = [];
32404        var btn = function(id,cmd , toggle, handler, html){
32405        
32406             var  event = toggle ? 'toggle' : 'click';
32407        
32408             var a = {
32409                 size : 'sm',
32410                 xtype: 'Button',
32411                 xns: Roo.bootstrap,
32412                 //glyphicon : id,
32413                 fa: id,
32414                 cmd : id || cmd,
32415                 enableToggle:toggle !== false,
32416                 html : html || '',
32417                 pressed : toggle ? false : null,
32418                 listeners : {}
32419             };
32420             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32421                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32422             };
32423             children.push(a);
32424             return a;
32425        }
32426        
32427     //    var cb_box = function...
32428         
32429         var style = {
32430                 xtype: 'Button',
32431                 size : 'sm',
32432                 xns: Roo.bootstrap,
32433                 fa : 'font',
32434                 //html : 'submit'
32435                 menu : {
32436                     xtype: 'Menu',
32437                     xns: Roo.bootstrap,
32438                     items:  []
32439                 }
32440         };
32441         Roo.each(this.formats, function(f) {
32442             style.menu.items.push({
32443                 xtype :'MenuItem',
32444                 xns: Roo.bootstrap,
32445                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32446                 tagname : f,
32447                 listeners : {
32448                     click : function()
32449                     {
32450                         editorcore.insertTag(this.tagname);
32451                         editor.focus();
32452                     }
32453                 }
32454                 
32455             });
32456         });
32457         children.push(style);   
32458         
32459         btn('bold',false,true);
32460         btn('italic',false,true);
32461         btn('align-left', 'justifyleft',true);
32462         btn('align-center', 'justifycenter',true);
32463         btn('align-right' , 'justifyright',true);
32464         btn('link', false, false, function(btn) {
32465             //Roo.log("create link?");
32466             var url = prompt(this.createLinkText, this.defaultLinkValue);
32467             if(url && url != 'http:/'+'/'){
32468                 this.editorcore.relayCmd('createlink', url);
32469             }
32470         }),
32471         btn('list','insertunorderedlist',true);
32472         btn('pencil', false,true, function(btn){
32473                 Roo.log(this);
32474                 this.toggleSourceEdit(btn.pressed);
32475         });
32476         
32477         if (this.editor.btns.length > 0) {
32478             for (var i = 0; i<this.editor.btns.length; i++) {
32479                 children.push(this.editor.btns[i]);
32480             }
32481         }
32482         
32483         /*
32484         var cog = {
32485                 xtype: 'Button',
32486                 size : 'sm',
32487                 xns: Roo.bootstrap,
32488                 glyphicon : 'cog',
32489                 //html : 'submit'
32490                 menu : {
32491                     xtype: 'Menu',
32492                     xns: Roo.bootstrap,
32493                     items:  []
32494                 }
32495         };
32496         
32497         cog.menu.items.push({
32498             xtype :'MenuItem',
32499             xns: Roo.bootstrap,
32500             html : Clean styles,
32501             tagname : f,
32502             listeners : {
32503                 click : function()
32504                 {
32505                     editorcore.insertTag(this.tagname);
32506                     editor.focus();
32507                 }
32508             }
32509             
32510         });
32511        */
32512         
32513          
32514        this.xtype = 'NavSimplebar';
32515         
32516         for(var i=0;i< children.length;i++) {
32517             
32518             this.buttons.add(this.addxtypeChild(children[i]));
32519             
32520         }
32521         
32522         editor.on('editorevent', this.updateToolbar, this);
32523     },
32524     onBtnClick : function(id)
32525     {
32526        this.editorcore.relayCmd(id);
32527        this.editorcore.focus();
32528     },
32529     
32530     /**
32531      * Protected method that will not generally be called directly. It triggers
32532      * a toolbar update by reading the markup state of the current selection in the editor.
32533      */
32534     updateToolbar: function(){
32535
32536         if(!this.editorcore.activated){
32537             this.editor.onFirstFocus(); // is this neeed?
32538             return;
32539         }
32540
32541         var btns = this.buttons; 
32542         var doc = this.editorcore.doc;
32543         btns.get('bold').setActive(doc.queryCommandState('bold'));
32544         btns.get('italic').setActive(doc.queryCommandState('italic'));
32545         //btns.get('underline').setActive(doc.queryCommandState('underline'));
32546         
32547         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32548         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32549         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32550         
32551         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32552         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32553          /*
32554         
32555         var ans = this.editorcore.getAllAncestors();
32556         if (this.formatCombo) {
32557             
32558             
32559             var store = this.formatCombo.store;
32560             this.formatCombo.setValue("");
32561             for (var i =0; i < ans.length;i++) {
32562                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32563                     // select it..
32564                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
32565                     break;
32566                 }
32567             }
32568         }
32569         
32570         
32571         
32572         // hides menus... - so this cant be on a menu...
32573         Roo.bootstrap.MenuMgr.hideAll();
32574         */
32575         Roo.bootstrap.menu.Manager.hideAll();
32576         //this.editorsyncValue();
32577     },
32578     onFirstFocus: function() {
32579         this.buttons.each(function(item){
32580            item.enable();
32581         });
32582     },
32583     toggleSourceEdit : function(sourceEditMode){
32584         
32585           
32586         if(sourceEditMode){
32587             Roo.log("disabling buttons");
32588            this.buttons.each( function(item){
32589                 if(item.cmd != 'pencil'){
32590                     item.disable();
32591                 }
32592             });
32593           
32594         }else{
32595             Roo.log("enabling buttons");
32596             if(this.editorcore.initialized){
32597                 this.buttons.each( function(item){
32598                     item.enable();
32599                 });
32600             }
32601             
32602         }
32603         Roo.log("calling toggole on editor");
32604         // tell the editor that it's been pressed..
32605         this.editor.toggleSourceEdit(sourceEditMode);
32606        
32607     }
32608 });
32609
32610
32611
32612
32613  
32614 /*
32615  * - LGPL
32616  */
32617
32618 /**
32619  * @class Roo.bootstrap.form.Markdown
32620  * @extends Roo.bootstrap.form.TextArea
32621  * Bootstrap Showdown editable area
32622  * @cfg {string} content
32623  * 
32624  * @constructor
32625  * Create a new Showdown
32626  */
32627
32628 Roo.bootstrap.form.Markdown = function(config){
32629     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
32630    
32631 };
32632
32633 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
32634     
32635     editing :false,
32636     
32637     initEvents : function()
32638     {
32639         
32640         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
32641         this.markdownEl = this.el.createChild({
32642             cls : 'roo-markdown-area'
32643         });
32644         this.inputEl().addClass('d-none');
32645         if (this.getValue() == '') {
32646             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32647             
32648         } else {
32649             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32650         }
32651         this.markdownEl.on('click', this.toggleTextEdit, this);
32652         this.on('blur', this.toggleTextEdit, this);
32653         this.on('specialkey', this.resizeTextArea, this);
32654     },
32655     
32656     toggleTextEdit : function()
32657     {
32658         var sh = this.markdownEl.getHeight();
32659         this.inputEl().addClass('d-none');
32660         this.markdownEl.addClass('d-none');
32661         if (!this.editing) {
32662             // show editor?
32663             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
32664             this.inputEl().removeClass('d-none');
32665             this.inputEl().focus();
32666             this.editing = true;
32667             return;
32668         }
32669         // show showdown...
32670         this.updateMarkdown();
32671         this.markdownEl.removeClass('d-none');
32672         this.editing = false;
32673         return;
32674     },
32675     updateMarkdown : function()
32676     {
32677         if (this.getValue() == '') {
32678             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32679             return;
32680         }
32681  
32682         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32683     },
32684     
32685     resizeTextArea: function () {
32686         
32687         var sh = 100;
32688         Roo.log([sh, this.getValue().split("\n").length * 30]);
32689         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
32690     },
32691     setValue : function(val)
32692     {
32693         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
32694         if (!this.editing) {
32695             this.updateMarkdown();
32696         }
32697         
32698     },
32699     focus : function()
32700     {
32701         if (!this.editing) {
32702             this.toggleTextEdit();
32703         }
32704         
32705     }
32706
32707
32708 });/*
32709  * Based on:
32710  * Ext JS Library 1.1.1
32711  * Copyright(c) 2006-2007, Ext JS, LLC.
32712  *
32713  * Originally Released Under LGPL - original licence link has changed is not relivant.
32714  *
32715  * Fork - LGPL
32716  * <script type="text/javascript">
32717  */
32718  
32719 /**
32720  * @class Roo.bootstrap.PagingToolbar
32721  * @extends Roo.bootstrap.nav.Simplebar
32722  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32723  * @constructor
32724  * Create a new PagingToolbar
32725  * @param {Object} config The config object
32726  * @param {Roo.data.Store} store
32727  */
32728 Roo.bootstrap.PagingToolbar = function(config)
32729 {
32730     // old args format still supported... - xtype is prefered..
32731         // created from xtype...
32732     
32733     this.ds = config.dataSource;
32734     
32735     if (config.store && !this.ds) {
32736         this.store= Roo.factory(config.store, Roo.data);
32737         this.ds = this.store;
32738         this.ds.xmodule = this.xmodule || false;
32739     }
32740     
32741     this.toolbarItems = [];
32742     if (config.items) {
32743         this.toolbarItems = config.items;
32744     }
32745     
32746     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
32747     
32748     this.cursor = 0;
32749     
32750     if (this.ds) { 
32751         this.bind(this.ds);
32752     }
32753     
32754     if (Roo.bootstrap.version == 4) {
32755         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
32756     } else {
32757         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
32758     }
32759     
32760 };
32761
32762 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
32763     /**
32764      * @cfg {Roo.bootstrap.Button} buttons[]
32765      * Buttons for the toolbar
32766      */
32767      /**
32768      * @cfg {Roo.data.Store} store
32769      * The underlying data store providing the paged data
32770      */
32771     /**
32772      * @cfg {String/HTMLElement/Element} container
32773      * container The id or element that will contain the toolbar
32774      */
32775     /**
32776      * @cfg {Boolean} displayInfo
32777      * True to display the displayMsg (defaults to false)
32778      */
32779     /**
32780      * @cfg {Number} pageSize
32781      * The number of records to display per page (defaults to 20)
32782      */
32783     pageSize: 20,
32784     /**
32785      * @cfg {String} displayMsg
32786      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32787      */
32788     displayMsg : 'Displaying {0} - {1} of {2}',
32789     /**
32790      * @cfg {String} emptyMsg
32791      * The message to display when no records are found (defaults to "No data to display")
32792      */
32793     emptyMsg : 'No data to display',
32794     /**
32795      * Customizable piece of the default paging text (defaults to "Page")
32796      * @type String
32797      */
32798     beforePageText : "Page",
32799     /**
32800      * Customizable piece of the default paging text (defaults to "of %0")
32801      * @type String
32802      */
32803     afterPageText : "of {0}",
32804     /**
32805      * Customizable piece of the default paging text (defaults to "First Page")
32806      * @type String
32807      */
32808     firstText : "First Page",
32809     /**
32810      * Customizable piece of the default paging text (defaults to "Previous Page")
32811      * @type String
32812      */
32813     prevText : "Previous Page",
32814     /**
32815      * Customizable piece of the default paging text (defaults to "Next Page")
32816      * @type String
32817      */
32818     nextText : "Next Page",
32819     /**
32820      * Customizable piece of the default paging text (defaults to "Last Page")
32821      * @type String
32822      */
32823     lastText : "Last Page",
32824     /**
32825      * Customizable piece of the default paging text (defaults to "Refresh")
32826      * @type String
32827      */
32828     refreshText : "Refresh",
32829
32830     buttons : false,
32831     // private
32832     onRender : function(ct, position) 
32833     {
32834         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
32835         this.navgroup.parentId = this.id;
32836         this.navgroup.onRender(this.el, null);
32837         // add the buttons to the navgroup
32838         
32839         if(this.displayInfo){
32840             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
32841             this.displayEl = this.el.select('.x-paging-info', true).first();
32842 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
32843 //            this.displayEl = navel.el.select('span',true).first();
32844         }
32845         
32846         var _this = this;
32847         
32848         if(this.buttons){
32849             Roo.each(_this.buttons, function(e){ // this might need to use render????
32850                Roo.factory(e).render(_this.el);
32851             });
32852         }
32853             
32854         Roo.each(_this.toolbarItems, function(e) {
32855             _this.navgroup.addItem(e);
32856         });
32857         
32858         
32859         this.first = this.navgroup.addItem({
32860             tooltip: this.firstText,
32861             cls: "prev btn-outline-secondary",
32862             html : ' <i class="fa fa-step-backward"></i>',
32863             disabled: true,
32864             preventDefault: true,
32865             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
32866         });
32867         
32868         this.prev =  this.navgroup.addItem({
32869             tooltip: this.prevText,
32870             cls: "prev btn-outline-secondary",
32871             html : ' <i class="fa fa-backward"></i>',
32872             disabled: true,
32873             preventDefault: true,
32874             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
32875         });
32876     //this.addSeparator();
32877         
32878         
32879         var field = this.navgroup.addItem( {
32880             tagtype : 'span',
32881             cls : 'x-paging-position  btn-outline-secondary',
32882              disabled: true,
32883             html : this.beforePageText  +
32884                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
32885                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
32886          } ); //?? escaped?
32887         
32888         this.field = field.el.select('input', true).first();
32889         this.field.on("keydown", this.onPagingKeydown, this);
32890         this.field.on("focus", function(){this.dom.select();});
32891     
32892     
32893         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
32894         //this.field.setHeight(18);
32895         //this.addSeparator();
32896         this.next = this.navgroup.addItem({
32897             tooltip: this.nextText,
32898             cls: "next btn-outline-secondary",
32899             html : ' <i class="fa fa-forward"></i>',
32900             disabled: true,
32901             preventDefault: true,
32902             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
32903         });
32904         this.last = this.navgroup.addItem({
32905             tooltip: this.lastText,
32906             html : ' <i class="fa fa-step-forward"></i>',
32907             cls: "next btn-outline-secondary",
32908             disabled: true,
32909             preventDefault: true,
32910             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
32911         });
32912     //this.addSeparator();
32913         this.loading = this.navgroup.addItem({
32914             tooltip: this.refreshText,
32915             cls: "btn-outline-secondary",
32916             html : ' <i class="fa fa-refresh"></i>',
32917             preventDefault: true,
32918             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
32919         });
32920         
32921     },
32922
32923     // private
32924     updateInfo : function(){
32925         if(this.displayEl){
32926             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
32927             var msg = count == 0 ?
32928                 this.emptyMsg :
32929                 String.format(
32930                     this.displayMsg,
32931                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32932                 );
32933             this.displayEl.update(msg);
32934         }
32935     },
32936
32937     // private
32938     onLoad : function(ds, r, o)
32939     {
32940         this.cursor = o.params && o.params.start ? o.params.start : 0;
32941         
32942         var d = this.getPageData(),
32943             ap = d.activePage,
32944             ps = d.pages;
32945         
32946         
32947         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
32948         this.field.dom.value = ap;
32949         this.first.setDisabled(ap == 1);
32950         this.prev.setDisabled(ap == 1);
32951         this.next.setDisabled(ap == ps);
32952         this.last.setDisabled(ap == ps);
32953         this.loading.enable();
32954         this.updateInfo();
32955     },
32956
32957     // private
32958     getPageData : function(){
32959         var total = this.ds.getTotalCount();
32960         return {
32961             total : total,
32962             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32963             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32964         };
32965     },
32966
32967     // private
32968     onLoadError : function(proxy, o){
32969         this.loading.enable();
32970         if (this.ds.events.loadexception.listeners.length  < 2) {
32971             // nothing has been assigned to loadexception except this...
32972             // so 
32973             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
32974
32975         }
32976     },
32977
32978     // private
32979     onPagingKeydown : function(e){
32980         var k = e.getKey();
32981         var d = this.getPageData();
32982         if(k == e.RETURN){
32983             var v = this.field.dom.value, pageNum;
32984             if(!v || isNaN(pageNum = parseInt(v, 10))){
32985                 this.field.dom.value = d.activePage;
32986                 return;
32987             }
32988             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32989             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32990             e.stopEvent();
32991         }
32992         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))
32993         {
32994           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32995           this.field.dom.value = pageNum;
32996           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32997           e.stopEvent();
32998         }
32999         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33000         {
33001           var v = this.field.dom.value, pageNum; 
33002           var increment = (e.shiftKey) ? 10 : 1;
33003           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33004                 increment *= -1;
33005           }
33006           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33007             this.field.dom.value = d.activePage;
33008             return;
33009           }
33010           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33011           {
33012             this.field.dom.value = parseInt(v, 10) + increment;
33013             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33014             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33015           }
33016           e.stopEvent();
33017         }
33018     },
33019
33020     // private
33021     beforeLoad : function(){
33022         if(this.loading){
33023             this.loading.disable();
33024         }
33025     },
33026
33027     // private
33028     onClick : function(which){
33029         
33030         var ds = this.ds;
33031         if (!ds) {
33032             return;
33033         }
33034         
33035         switch(which){
33036             case "first":
33037                 ds.load({params:{start: 0, limit: this.pageSize}});
33038             break;
33039             case "prev":
33040                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33041             break;
33042             case "next":
33043                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33044             break;
33045             case "last":
33046                 var total = ds.getTotalCount();
33047                 var extra = total % this.pageSize;
33048                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33049                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33050             break;
33051             case "refresh":
33052                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33053             break;
33054         }
33055     },
33056
33057     /**
33058      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33059      * @param {Roo.data.Store} store The data store to unbind
33060      */
33061     unbind : function(ds){
33062         ds.un("beforeload", this.beforeLoad, this);
33063         ds.un("load", this.onLoad, this);
33064         ds.un("loadexception", this.onLoadError, this);
33065         ds.un("remove", this.updateInfo, this);
33066         ds.un("add", this.updateInfo, this);
33067         this.ds = undefined;
33068     },
33069
33070     /**
33071      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33072      * @param {Roo.data.Store} store The data store to bind
33073      */
33074     bind : function(ds){
33075         ds.on("beforeload", this.beforeLoad, this);
33076         ds.on("load", this.onLoad, this);
33077         ds.on("loadexception", this.onLoadError, this);
33078         ds.on("remove", this.updateInfo, this);
33079         ds.on("add", this.updateInfo, this);
33080         this.ds = ds;
33081     }
33082 });/*
33083  * - LGPL
33084  *
33085  * element
33086  * 
33087  */
33088
33089 /**
33090  * @class Roo.bootstrap.MessageBar
33091  * @extends Roo.bootstrap.Component
33092  * Bootstrap MessageBar class
33093  * @cfg {String} html contents of the MessageBar
33094  * @cfg {String} weight (info | success | warning | danger) default info
33095  * @cfg {String} beforeClass insert the bar before the given class
33096  * @cfg {Boolean} closable (true | false) default false
33097  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33098  * 
33099  * @constructor
33100  * Create a new Element
33101  * @param {Object} config The config object
33102  */
33103
33104 Roo.bootstrap.MessageBar = function(config){
33105     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33106 };
33107
33108 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33109     
33110     html: '',
33111     weight: 'info',
33112     closable: false,
33113     fixed: false,
33114     beforeClass: 'bootstrap-sticky-wrap',
33115     
33116     getAutoCreate : function(){
33117         
33118         var cfg = {
33119             tag: 'div',
33120             cls: 'alert alert-dismissable alert-' + this.weight,
33121             cn: [
33122                 {
33123                     tag: 'span',
33124                     cls: 'message',
33125                     html: this.html || ''
33126                 }
33127             ]
33128         };
33129         
33130         if(this.fixed){
33131             cfg.cls += ' alert-messages-fixed';
33132         }
33133         
33134         if(this.closable){
33135             cfg.cn.push({
33136                 tag: 'button',
33137                 cls: 'close',
33138                 html: 'x'
33139             });
33140         }
33141         
33142         return cfg;
33143     },
33144     
33145     onRender : function(ct, position)
33146     {
33147         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33148         
33149         if(!this.el){
33150             var cfg = Roo.apply({},  this.getAutoCreate());
33151             cfg.id = Roo.id();
33152             
33153             if (this.cls) {
33154                 cfg.cls += ' ' + this.cls;
33155             }
33156             if (this.style) {
33157                 cfg.style = this.style;
33158             }
33159             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33160             
33161             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33162         }
33163         
33164         this.el.select('>button.close').on('click', this.hide, this);
33165         
33166     },
33167     
33168     show : function()
33169     {
33170         if (!this.rendered) {
33171             this.render();
33172         }
33173         
33174         this.el.show();
33175         
33176         this.fireEvent('show', this);
33177         
33178     },
33179     
33180     hide : function()
33181     {
33182         if (!this.rendered) {
33183             this.render();
33184         }
33185         
33186         this.el.hide();
33187         
33188         this.fireEvent('hide', this);
33189     },
33190     
33191     update : function()
33192     {
33193 //        var e = this.el.dom.firstChild;
33194 //        
33195 //        if(this.closable){
33196 //            e = e.nextSibling;
33197 //        }
33198 //        
33199 //        e.data = this.html || '';
33200
33201         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33202     }
33203    
33204 });
33205
33206  
33207
33208      /*
33209  * - LGPL
33210  *
33211  * Graph
33212  * 
33213  */
33214
33215
33216 /**
33217  * @class Roo.bootstrap.Graph
33218  * @extends Roo.bootstrap.Component
33219  * Bootstrap Graph class
33220 > Prameters
33221  -sm {number} sm 4
33222  -md {number} md 5
33223  @cfg {String} graphtype  bar | vbar | pie
33224  @cfg {number} g_x coodinator | centre x (pie)
33225  @cfg {number} g_y coodinator | centre y (pie)
33226  @cfg {number} g_r radius (pie)
33227  @cfg {number} g_height height of the chart (respected by all elements in the set)
33228  @cfg {number} g_width width of the chart (respected by all elements in the set)
33229  @cfg {Object} title The title of the chart
33230     
33231  -{Array}  values
33232  -opts (object) options for the chart 
33233      o {
33234      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33235      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33236      o vgutter (number)
33237      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.
33238      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33239      o to
33240      o stretch (boolean)
33241      o }
33242  -opts (object) options for the pie
33243      o{
33244      o cut
33245      o startAngle (number)
33246      o endAngle (number)
33247      } 
33248  *
33249  * @constructor
33250  * Create a new Input
33251  * @param {Object} config The config object
33252  */
33253
33254 Roo.bootstrap.Graph = function(config){
33255     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33256     
33257     this.addEvents({
33258         // img events
33259         /**
33260          * @event click
33261          * The img click event for the img.
33262          * @param {Roo.EventObject} e
33263          */
33264         "click" : true
33265     });
33266 };
33267
33268 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33269     
33270     sm: 4,
33271     md: 5,
33272     graphtype: 'bar',
33273     g_height: 250,
33274     g_width: 400,
33275     g_x: 50,
33276     g_y: 50,
33277     g_r: 30,
33278     opts:{
33279         //g_colors: this.colors,
33280         g_type: 'soft',
33281         g_gutter: '20%'
33282
33283     },
33284     title : false,
33285
33286     getAutoCreate : function(){
33287         
33288         var cfg = {
33289             tag: 'div',
33290             html : null
33291         };
33292         
33293         
33294         return  cfg;
33295     },
33296
33297     onRender : function(ct,position){
33298         
33299         
33300         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33301         
33302         if (typeof(Raphael) == 'undefined') {
33303             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33304             return;
33305         }
33306         
33307         this.raphael = Raphael(this.el.dom);
33308         
33309                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33310                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33311                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33312                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33313                 /*
33314                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33315                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33316                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33317                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33318                 
33319                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33320                 r.barchart(330, 10, 300, 220, data1);
33321                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33322                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33323                 */
33324                 
33325                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33326                 // r.barchart(30, 30, 560, 250,  xdata, {
33327                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33328                 //     axis : "0 0 1 1",
33329                 //     axisxlabels :  xdata
33330                 //     //yvalues : cols,
33331                    
33332                 // });
33333 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33334 //        
33335 //        this.load(null,xdata,{
33336 //                axis : "0 0 1 1",
33337 //                axisxlabels :  xdata
33338 //                });
33339
33340     },
33341
33342     load : function(graphtype,xdata,opts)
33343     {
33344         this.raphael.clear();
33345         if(!graphtype) {
33346             graphtype = this.graphtype;
33347         }
33348         if(!opts){
33349             opts = this.opts;
33350         }
33351         var r = this.raphael,
33352             fin = function () {
33353                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33354             },
33355             fout = function () {
33356                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33357             },
33358             pfin = function() {
33359                 this.sector.stop();
33360                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33361
33362                 if (this.label) {
33363                     this.label[0].stop();
33364                     this.label[0].attr({ r: 7.5 });
33365                     this.label[1].attr({ "font-weight": 800 });
33366                 }
33367             },
33368             pfout = function() {
33369                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33370
33371                 if (this.label) {
33372                     this.label[0].animate({ r: 5 }, 500, "bounce");
33373                     this.label[1].attr({ "font-weight": 400 });
33374                 }
33375             };
33376
33377         switch(graphtype){
33378             case 'bar':
33379                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33380                 break;
33381             case 'hbar':
33382                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33383                 break;
33384             case 'pie':
33385 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33386 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33387 //            
33388                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33389                 
33390                 break;
33391
33392         }
33393         
33394         if(this.title){
33395             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33396         }
33397         
33398     },
33399     
33400     setTitle: function(o)
33401     {
33402         this.title = o;
33403     },
33404     
33405     initEvents: function() {
33406         
33407         if(!this.href){
33408             this.el.on('click', this.onClick, this);
33409         }
33410     },
33411     
33412     onClick : function(e)
33413     {
33414         Roo.log('img onclick');
33415         this.fireEvent('click', this, e);
33416     }
33417    
33418 });
33419
33420  
33421 Roo.bootstrap.dash = {};/*
33422  * - LGPL
33423  *
33424  * numberBox
33425  * 
33426  */
33427 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33428
33429 /**
33430  * @class Roo.bootstrap.dash.NumberBox
33431  * @extends Roo.bootstrap.Component
33432  * Bootstrap NumberBox class
33433  * @cfg {String} headline Box headline
33434  * @cfg {String} content Box content
33435  * @cfg {String} icon Box icon
33436  * @cfg {String} footer Footer text
33437  * @cfg {String} fhref Footer href
33438  * 
33439  * @constructor
33440  * Create a new NumberBox
33441  * @param {Object} config The config object
33442  */
33443
33444
33445 Roo.bootstrap.dash.NumberBox = function(config){
33446     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33447     
33448 };
33449
33450 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33451     
33452     headline : '',
33453     content : '',
33454     icon : '',
33455     footer : '',
33456     fhref : '',
33457     ficon : '',
33458     
33459     getAutoCreate : function(){
33460         
33461         var cfg = {
33462             tag : 'div',
33463             cls : 'small-box ',
33464             cn : [
33465                 {
33466                     tag : 'div',
33467                     cls : 'inner',
33468                     cn :[
33469                         {
33470                             tag : 'h3',
33471                             cls : 'roo-headline',
33472                             html : this.headline
33473                         },
33474                         {
33475                             tag : 'p',
33476                             cls : 'roo-content',
33477                             html : this.content
33478                         }
33479                     ]
33480                 }
33481             ]
33482         };
33483         
33484         if(this.icon){
33485             cfg.cn.push({
33486                 tag : 'div',
33487                 cls : 'icon',
33488                 cn :[
33489                     {
33490                         tag : 'i',
33491                         cls : 'ion ' + this.icon
33492                     }
33493                 ]
33494             });
33495         }
33496         
33497         if(this.footer){
33498             var footer = {
33499                 tag : 'a',
33500                 cls : 'small-box-footer',
33501                 href : this.fhref || '#',
33502                 html : this.footer
33503             };
33504             
33505             cfg.cn.push(footer);
33506             
33507         }
33508         
33509         return  cfg;
33510     },
33511
33512     onRender : function(ct,position){
33513         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33514
33515
33516        
33517                 
33518     },
33519
33520     setHeadline: function (value)
33521     {
33522         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33523     },
33524     
33525     setFooter: function (value, href)
33526     {
33527         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33528         
33529         if(href){
33530             this.el.select('a.small-box-footer',true).first().attr('href', href);
33531         }
33532         
33533     },
33534
33535     setContent: function (value)
33536     {
33537         this.el.select('.roo-content',true).first().dom.innerHTML = value;
33538     },
33539
33540     initEvents: function() 
33541     {   
33542         
33543     }
33544     
33545 });
33546
33547  
33548 /*
33549  * - LGPL
33550  *
33551  * TabBox
33552  * 
33553  */
33554 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33555
33556 /**
33557  * @class Roo.bootstrap.dash.TabBox
33558  * @extends Roo.bootstrap.Component
33559  * @children Roo.bootstrap.dash.TabPane
33560  * Bootstrap TabBox class
33561  * @cfg {String} title Title of the TabBox
33562  * @cfg {String} icon Icon of the TabBox
33563  * @cfg {Boolean} showtabs (true|false) show the tabs default true
33564  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33565  * 
33566  * @constructor
33567  * Create a new TabBox
33568  * @param {Object} config The config object
33569  */
33570
33571
33572 Roo.bootstrap.dash.TabBox = function(config){
33573     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
33574     this.addEvents({
33575         // raw events
33576         /**
33577          * @event addpane
33578          * When a pane is added
33579          * @param {Roo.bootstrap.dash.TabPane} pane
33580          */
33581         "addpane" : true,
33582         /**
33583          * @event activatepane
33584          * When a pane is activated
33585          * @param {Roo.bootstrap.dash.TabPane} pane
33586          */
33587         "activatepane" : true
33588         
33589          
33590     });
33591     
33592     this.panes = [];
33593 };
33594
33595 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
33596
33597     title : '',
33598     icon : false,
33599     showtabs : true,
33600     tabScrollable : false,
33601     
33602     getChildContainer : function()
33603     {
33604         return this.el.select('.tab-content', true).first();
33605     },
33606     
33607     getAutoCreate : function(){
33608         
33609         var header = {
33610             tag: 'li',
33611             cls: 'pull-left header',
33612             html: this.title,
33613             cn : []
33614         };
33615         
33616         if(this.icon){
33617             header.cn.push({
33618                 tag: 'i',
33619                 cls: 'fa ' + this.icon
33620             });
33621         }
33622         
33623         var h = {
33624             tag: 'ul',
33625             cls: 'nav nav-tabs pull-right',
33626             cn: [
33627                 header
33628             ]
33629         };
33630         
33631         if(this.tabScrollable){
33632             h = {
33633                 tag: 'div',
33634                 cls: 'tab-header',
33635                 cn: [
33636                     {
33637                         tag: 'ul',
33638                         cls: 'nav nav-tabs pull-right',
33639                         cn: [
33640                             header
33641                         ]
33642                     }
33643                 ]
33644             };
33645         }
33646         
33647         var cfg = {
33648             tag: 'div',
33649             cls: 'nav-tabs-custom',
33650             cn: [
33651                 h,
33652                 {
33653                     tag: 'div',
33654                     cls: 'tab-content no-padding',
33655                     cn: []
33656                 }
33657             ]
33658         };
33659
33660         return  cfg;
33661     },
33662     initEvents : function()
33663     {
33664         //Roo.log('add add pane handler');
33665         this.on('addpane', this.onAddPane, this);
33666     },
33667      /**
33668      * Updates the box title
33669      * @param {String} html to set the title to.
33670      */
33671     setTitle : function(value)
33672     {
33673         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
33674     },
33675     onAddPane : function(pane)
33676     {
33677         this.panes.push(pane);
33678         //Roo.log('addpane');
33679         //Roo.log(pane);
33680         // tabs are rendere left to right..
33681         if(!this.showtabs){
33682             return;
33683         }
33684         
33685         var ctr = this.el.select('.nav-tabs', true).first();
33686          
33687          
33688         var existing = ctr.select('.nav-tab',true);
33689         var qty = existing.getCount();;
33690         
33691         
33692         var tab = ctr.createChild({
33693             tag : 'li',
33694             cls : 'nav-tab' + (qty ? '' : ' active'),
33695             cn : [
33696                 {
33697                     tag : 'a',
33698                     href:'#',
33699                     html : pane.title
33700                 }
33701             ]
33702         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
33703         pane.tab = tab;
33704         
33705         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
33706         if (!qty) {
33707             pane.el.addClass('active');
33708         }
33709         
33710                 
33711     },
33712     onTabClick : function(ev,un,ob,pane)
33713     {
33714         //Roo.log('tab - prev default');
33715         ev.preventDefault();
33716         
33717         
33718         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
33719         pane.tab.addClass('active');
33720         //Roo.log(pane.title);
33721         this.getChildContainer().select('.tab-pane',true).removeClass('active');
33722         // technically we should have a deactivate event.. but maybe add later.
33723         // and it should not de-activate the selected tab...
33724         this.fireEvent('activatepane', pane);
33725         pane.el.addClass('active');
33726         pane.fireEvent('activate');
33727         
33728         
33729     },
33730     
33731     getActivePane : function()
33732     {
33733         var r = false;
33734         Roo.each(this.panes, function(p) {
33735             if(p.el.hasClass('active')){
33736                 r = p;
33737                 return false;
33738             }
33739             
33740             return;
33741         });
33742         
33743         return r;
33744     }
33745     
33746     
33747 });
33748
33749  
33750 /*
33751  * - LGPL
33752  *
33753  * Tab pane
33754  * 
33755  */
33756 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33757 /**
33758  * @class Roo.bootstrap.TabPane
33759  * @extends Roo.bootstrap.Component
33760  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
33761  * Bootstrap TabPane class
33762  * @cfg {Boolean} active (false | true) Default false
33763  * @cfg {String} title title of panel
33764
33765  * 
33766  * @constructor
33767  * Create a new TabPane
33768  * @param {Object} config The config object
33769  */
33770
33771 Roo.bootstrap.dash.TabPane = function(config){
33772     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
33773     
33774     this.addEvents({
33775         // raw events
33776         /**
33777          * @event activate
33778          * When a pane is activated
33779          * @param {Roo.bootstrap.dash.TabPane} pane
33780          */
33781         "activate" : true
33782          
33783     });
33784 };
33785
33786 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
33787     
33788     active : false,
33789     title : '',
33790     
33791     // the tabBox that this is attached to.
33792     tab : false,
33793      
33794     getAutoCreate : function() 
33795     {
33796         var cfg = {
33797             tag: 'div',
33798             cls: 'tab-pane'
33799         };
33800         
33801         if(this.active){
33802             cfg.cls += ' active';
33803         }
33804         
33805         return cfg;
33806     },
33807     initEvents  : function()
33808     {
33809         //Roo.log('trigger add pane handler');
33810         this.parent().fireEvent('addpane', this)
33811     },
33812     
33813      /**
33814      * Updates the tab title 
33815      * @param {String} html to set the title to.
33816      */
33817     setTitle: function(str)
33818     {
33819         if (!this.tab) {
33820             return;
33821         }
33822         this.title = str;
33823         this.tab.select('a', true).first().dom.innerHTML = str;
33824         
33825     }
33826     
33827     
33828     
33829 });
33830
33831  
33832
33833
33834  /*
33835  * - LGPL
33836  *
33837  * Tooltip
33838  * 
33839  */
33840
33841 /**
33842  * @class Roo.bootstrap.Tooltip
33843  * Bootstrap Tooltip class
33844  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
33845  * to determine which dom element triggers the tooltip.
33846  * 
33847  * It needs to add support for additional attributes like tooltip-position
33848  * 
33849  * @constructor
33850  * Create a new Toolti
33851  * @param {Object} config The config object
33852  */
33853
33854 Roo.bootstrap.Tooltip = function(config){
33855     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
33856     
33857     this.alignment = Roo.bootstrap.Tooltip.alignment;
33858     
33859     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
33860         this.alignment = config.alignment;
33861     }
33862     
33863 };
33864
33865 Roo.apply(Roo.bootstrap.Tooltip, {
33866     /**
33867      * @function init initialize tooltip monitoring.
33868      * @static
33869      */
33870     currentEl : false,
33871     currentTip : false,
33872     currentRegion : false,
33873     
33874     //  init : delay?
33875     
33876     init : function()
33877     {
33878         Roo.get(document).on('mouseover', this.enter ,this);
33879         Roo.get(document).on('mouseout', this.leave, this);
33880          
33881         
33882         this.currentTip = new Roo.bootstrap.Tooltip();
33883     },
33884     
33885     enter : function(ev)
33886     {
33887         var dom = ev.getTarget();
33888         
33889         //Roo.log(['enter',dom]);
33890         var el = Roo.fly(dom);
33891         if (this.currentEl) {
33892             //Roo.log(dom);
33893             //Roo.log(this.currentEl);
33894             //Roo.log(this.currentEl.contains(dom));
33895             if (this.currentEl == el) {
33896                 return;
33897             }
33898             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
33899                 return;
33900             }
33901
33902         }
33903         
33904         if (this.currentTip.el) {
33905             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
33906         }    
33907         //Roo.log(ev);
33908         
33909         if(!el || el.dom == document){
33910             return;
33911         }
33912         
33913         var bindEl = el; 
33914         var pel = false;
33915         if (!el.attr('tooltip')) {
33916             pel = el.findParent("[tooltip]");
33917             if (pel) {
33918                 bindEl = Roo.get(pel);
33919             }
33920         }
33921         
33922        
33923         
33924         // you can not look for children, as if el is the body.. then everythign is the child..
33925         if (!pel && !el.attr('tooltip')) { //
33926             if (!el.select("[tooltip]").elements.length) {
33927                 return;
33928             }
33929             // is the mouse over this child...?
33930             bindEl = el.select("[tooltip]").first();
33931             var xy = ev.getXY();
33932             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
33933                 //Roo.log("not in region.");
33934                 return;
33935             }
33936             //Roo.log("child element over..");
33937             
33938         }
33939         this.currentEl = el;
33940         this.currentTip.bind(bindEl);
33941         this.currentRegion = Roo.lib.Region.getRegion(dom);
33942         this.currentTip.enter();
33943         
33944     },
33945     leave : function(ev)
33946     {
33947         var dom = ev.getTarget();
33948         //Roo.log(['leave',dom]);
33949         if (!this.currentEl) {
33950             return;
33951         }
33952         
33953         
33954         if (dom != this.currentEl.dom) {
33955             return;
33956         }
33957         var xy = ev.getXY();
33958         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
33959             return;
33960         }
33961         // only activate leave if mouse cursor is outside... bounding box..
33962         
33963         
33964         
33965         
33966         if (this.currentTip) {
33967             this.currentTip.leave();
33968         }
33969         //Roo.log('clear currentEl');
33970         this.currentEl = false;
33971         
33972         
33973     },
33974     alignment : {
33975         'left' : ['r-l', [-2,0], 'right'],
33976         'right' : ['l-r', [2,0], 'left'],
33977         'bottom' : ['t-b', [0,2], 'top'],
33978         'top' : [ 'b-t', [0,-2], 'bottom']
33979     }
33980     
33981 });
33982
33983
33984 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
33985     
33986     
33987     bindEl : false,
33988     
33989     delay : null, // can be { show : 300 , hide: 500}
33990     
33991     timeout : null,
33992     
33993     hoverState : null, //???
33994     
33995     placement : 'bottom', 
33996     
33997     alignment : false,
33998     
33999     getAutoCreate : function(){
34000     
34001         var cfg = {
34002            cls : 'tooltip',   
34003            role : 'tooltip',
34004            cn : [
34005                 {
34006                     cls : 'tooltip-arrow arrow'
34007                 },
34008                 {
34009                     cls : 'tooltip-inner'
34010                 }
34011            ]
34012         };
34013         
34014         return cfg;
34015     },
34016     bind : function(el)
34017     {
34018         this.bindEl = el;
34019     },
34020     
34021     initEvents : function()
34022     {
34023         this.arrowEl = this.el.select('.arrow', true).first();
34024         this.innerEl = this.el.select('.tooltip-inner', true).first();
34025     },
34026     
34027     enter : function () {
34028        
34029         if (this.timeout != null) {
34030             clearTimeout(this.timeout);
34031         }
34032         
34033         this.hoverState = 'in';
34034          //Roo.log("enter - show");
34035         if (!this.delay || !this.delay.show) {
34036             this.show();
34037             return;
34038         }
34039         var _t = this;
34040         this.timeout = setTimeout(function () {
34041             if (_t.hoverState == 'in') {
34042                 _t.show();
34043             }
34044         }, this.delay.show);
34045     },
34046     leave : function()
34047     {
34048         clearTimeout(this.timeout);
34049     
34050         this.hoverState = 'out';
34051          if (!this.delay || !this.delay.hide) {
34052             this.hide();
34053             return;
34054         }
34055        
34056         var _t = this;
34057         this.timeout = setTimeout(function () {
34058             //Roo.log("leave - timeout");
34059             
34060             if (_t.hoverState == 'out') {
34061                 _t.hide();
34062                 Roo.bootstrap.Tooltip.currentEl = false;
34063             }
34064         }, delay);
34065     },
34066     
34067     show : function (msg)
34068     {
34069         if (!this.el) {
34070             this.render(document.body);
34071         }
34072         // set content.
34073         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34074         
34075         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34076         
34077         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34078         
34079         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34080                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34081
34082         if(this.bindEl.attr('tooltip-class')) {
34083             this.el.addClass(this.bindEl.attr('tooltip-class'));
34084         }
34085         
34086         var placement = typeof this.placement == 'function' ?
34087             this.placement.call(this, this.el, on_el) :
34088             this.placement;
34089         
34090         if(this.bindEl.attr('tooltip-placement')) {
34091             placement = this.bindEl.attr('tooltip-placement');
34092         }
34093             
34094         var autoToken = /\s?auto?\s?/i;
34095         var autoPlace = autoToken.test(placement);
34096         if (autoPlace) {
34097             placement = placement.replace(autoToken, '') || 'top';
34098         }
34099         
34100         //this.el.detach()
34101         //this.el.setXY([0,0]);
34102         this.el.show();
34103         //this.el.dom.style.display='block';
34104         
34105         //this.el.appendTo(on_el);
34106         
34107         var p = this.getPosition();
34108         var box = this.el.getBox();
34109         
34110         if (autoPlace) {
34111             // fixme..
34112         }
34113         
34114         var align = this.alignment[placement];
34115         
34116         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34117         
34118         if(placement == 'top' || placement == 'bottom'){
34119             if(xy[0] < 0){
34120                 placement = 'right';
34121             }
34122             
34123             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34124                 placement = 'left';
34125             }
34126             
34127             var scroll = Roo.select('body', true).first().getScroll();
34128             
34129             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34130                 placement = 'top';
34131             }
34132             
34133             align = this.alignment[placement];
34134             
34135             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34136             
34137         }
34138         
34139         var elems = document.getElementsByTagName('div');
34140         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34141         for (var i = 0; i < elems.length; i++) {
34142           var zindex = Number.parseInt(
34143                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34144                 10
34145           );
34146           if (zindex > highest) {
34147             highest = zindex;
34148           }
34149         }
34150         
34151         
34152         
34153         this.el.dom.style.zIndex = highest;
34154         
34155         this.el.alignTo(this.bindEl, align[0],align[1]);
34156         //var arrow = this.el.select('.arrow',true).first();
34157         //arrow.set(align[2], 
34158         
34159         this.el.addClass(placement);
34160         this.el.addClass("bs-tooltip-"+ placement);
34161         
34162         this.el.addClass('in fade show');
34163         
34164         this.hoverState = null;
34165         
34166         if (this.el.hasClass('fade')) {
34167             // fade it?
34168         }
34169         
34170         
34171         
34172         
34173         
34174     },
34175     hide : function()
34176     {
34177          
34178         if (!this.el) {
34179             return;
34180         }
34181         //this.el.setXY([0,0]);
34182         if(this.bindEl.attr('tooltip-class')) {
34183             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34184         }
34185         this.el.removeClass(['show', 'in']);
34186         //this.el.hide();
34187         
34188     }
34189     
34190 });
34191  
34192
34193  /*
34194  * - LGPL
34195  *
34196  * Location Picker
34197  * 
34198  */
34199
34200 /**
34201  * @class Roo.bootstrap.LocationPicker
34202  * @extends Roo.bootstrap.Component
34203  * Bootstrap LocationPicker class
34204  * @cfg {Number} latitude Position when init default 0
34205  * @cfg {Number} longitude Position when init default 0
34206  * @cfg {Number} zoom default 15
34207  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34208  * @cfg {Boolean} mapTypeControl default false
34209  * @cfg {Boolean} disableDoubleClickZoom default false
34210  * @cfg {Boolean} scrollwheel default true
34211  * @cfg {Boolean} streetViewControl default false
34212  * @cfg {Number} radius default 0
34213  * @cfg {String} locationName
34214  * @cfg {Boolean} draggable default true
34215  * @cfg {Boolean} enableAutocomplete default false
34216  * @cfg {Boolean} enableReverseGeocode default true
34217  * @cfg {String} markerTitle
34218  * 
34219  * @constructor
34220  * Create a new LocationPicker
34221  * @param {Object} config The config object
34222  */
34223
34224
34225 Roo.bootstrap.LocationPicker = function(config){
34226     
34227     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34228     
34229     this.addEvents({
34230         /**
34231          * @event initial
34232          * Fires when the picker initialized.
34233          * @param {Roo.bootstrap.LocationPicker} this
34234          * @param {Google Location} location
34235          */
34236         initial : true,
34237         /**
34238          * @event positionchanged
34239          * Fires when the picker position changed.
34240          * @param {Roo.bootstrap.LocationPicker} this
34241          * @param {Google Location} location
34242          */
34243         positionchanged : true,
34244         /**
34245          * @event resize
34246          * Fires when the map resize.
34247          * @param {Roo.bootstrap.LocationPicker} this
34248          */
34249         resize : true,
34250         /**
34251          * @event show
34252          * Fires when the map show.
34253          * @param {Roo.bootstrap.LocationPicker} this
34254          */
34255         show : true,
34256         /**
34257          * @event hide
34258          * Fires when the map hide.
34259          * @param {Roo.bootstrap.LocationPicker} this
34260          */
34261         hide : true,
34262         /**
34263          * @event mapClick
34264          * Fires when click the map.
34265          * @param {Roo.bootstrap.LocationPicker} this
34266          * @param {Map event} e
34267          */
34268         mapClick : true,
34269         /**
34270          * @event mapRightClick
34271          * Fires when right click the map.
34272          * @param {Roo.bootstrap.LocationPicker} this
34273          * @param {Map event} e
34274          */
34275         mapRightClick : true,
34276         /**
34277          * @event markerClick
34278          * Fires when click the marker.
34279          * @param {Roo.bootstrap.LocationPicker} this
34280          * @param {Map event} e
34281          */
34282         markerClick : true,
34283         /**
34284          * @event markerRightClick
34285          * Fires when right click the marker.
34286          * @param {Roo.bootstrap.LocationPicker} this
34287          * @param {Map event} e
34288          */
34289         markerRightClick : true,
34290         /**
34291          * @event OverlayViewDraw
34292          * Fires when OverlayView Draw
34293          * @param {Roo.bootstrap.LocationPicker} this
34294          */
34295         OverlayViewDraw : true,
34296         /**
34297          * @event OverlayViewOnAdd
34298          * Fires when OverlayView Draw
34299          * @param {Roo.bootstrap.LocationPicker} this
34300          */
34301         OverlayViewOnAdd : true,
34302         /**
34303          * @event OverlayViewOnRemove
34304          * Fires when OverlayView Draw
34305          * @param {Roo.bootstrap.LocationPicker} this
34306          */
34307         OverlayViewOnRemove : true,
34308         /**
34309          * @event OverlayViewShow
34310          * Fires when OverlayView Draw
34311          * @param {Roo.bootstrap.LocationPicker} this
34312          * @param {Pixel} cpx
34313          */
34314         OverlayViewShow : true,
34315         /**
34316          * @event OverlayViewHide
34317          * Fires when OverlayView Draw
34318          * @param {Roo.bootstrap.LocationPicker} this
34319          */
34320         OverlayViewHide : true,
34321         /**
34322          * @event loadexception
34323          * Fires when load google lib failed.
34324          * @param {Roo.bootstrap.LocationPicker} this
34325          */
34326         loadexception : true
34327     });
34328         
34329 };
34330
34331 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34332     
34333     gMapContext: false,
34334     
34335     latitude: 0,
34336     longitude: 0,
34337     zoom: 15,
34338     mapTypeId: false,
34339     mapTypeControl: false,
34340     disableDoubleClickZoom: false,
34341     scrollwheel: true,
34342     streetViewControl: false,
34343     radius: 0,
34344     locationName: '',
34345     draggable: true,
34346     enableAutocomplete: false,
34347     enableReverseGeocode: true,
34348     markerTitle: '',
34349     
34350     getAutoCreate: function()
34351     {
34352
34353         var cfg = {
34354             tag: 'div',
34355             cls: 'roo-location-picker'
34356         };
34357         
34358         return cfg
34359     },
34360     
34361     initEvents: function(ct, position)
34362     {       
34363         if(!this.el.getWidth() || this.isApplied()){
34364             return;
34365         }
34366         
34367         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34368         
34369         this.initial();
34370     },
34371     
34372     initial: function()
34373     {
34374         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34375             this.fireEvent('loadexception', this);
34376             return;
34377         }
34378         
34379         if(!this.mapTypeId){
34380             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34381         }
34382         
34383         this.gMapContext = this.GMapContext();
34384         
34385         this.initOverlayView();
34386         
34387         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34388         
34389         var _this = this;
34390                 
34391         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34392             _this.setPosition(_this.gMapContext.marker.position);
34393         });
34394         
34395         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34396             _this.fireEvent('mapClick', this, event);
34397             
34398         });
34399
34400         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34401             _this.fireEvent('mapRightClick', this, event);
34402             
34403         });
34404         
34405         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34406             _this.fireEvent('markerClick', this, event);
34407             
34408         });
34409
34410         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34411             _this.fireEvent('markerRightClick', this, event);
34412             
34413         });
34414         
34415         this.setPosition(this.gMapContext.location);
34416         
34417         this.fireEvent('initial', this, this.gMapContext.location);
34418     },
34419     
34420     initOverlayView: function()
34421     {
34422         var _this = this;
34423         
34424         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34425             
34426             draw: function()
34427             {
34428                 _this.fireEvent('OverlayViewDraw', _this);
34429             },
34430             
34431             onAdd: function()
34432             {
34433                 _this.fireEvent('OverlayViewOnAdd', _this);
34434             },
34435             
34436             onRemove: function()
34437             {
34438                 _this.fireEvent('OverlayViewOnRemove', _this);
34439             },
34440             
34441             show: function(cpx)
34442             {
34443                 _this.fireEvent('OverlayViewShow', _this, cpx);
34444             },
34445             
34446             hide: function()
34447             {
34448                 _this.fireEvent('OverlayViewHide', _this);
34449             }
34450             
34451         });
34452     },
34453     
34454     fromLatLngToContainerPixel: function(event)
34455     {
34456         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34457     },
34458     
34459     isApplied: function() 
34460     {
34461         return this.getGmapContext() == false ? false : true;
34462     },
34463     
34464     getGmapContext: function() 
34465     {
34466         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34467     },
34468     
34469     GMapContext: function() 
34470     {
34471         var position = new google.maps.LatLng(this.latitude, this.longitude);
34472         
34473         var _map = new google.maps.Map(this.el.dom, {
34474             center: position,
34475             zoom: this.zoom,
34476             mapTypeId: this.mapTypeId,
34477             mapTypeControl: this.mapTypeControl,
34478             disableDoubleClickZoom: this.disableDoubleClickZoom,
34479             scrollwheel: this.scrollwheel,
34480             streetViewControl: this.streetViewControl,
34481             locationName: this.locationName,
34482             draggable: this.draggable,
34483             enableAutocomplete: this.enableAutocomplete,
34484             enableReverseGeocode: this.enableReverseGeocode
34485         });
34486         
34487         var _marker = new google.maps.Marker({
34488             position: position,
34489             map: _map,
34490             title: this.markerTitle,
34491             draggable: this.draggable
34492         });
34493         
34494         return {
34495             map: _map,
34496             marker: _marker,
34497             circle: null,
34498             location: position,
34499             radius: this.radius,
34500             locationName: this.locationName,
34501             addressComponents: {
34502                 formatted_address: null,
34503                 addressLine1: null,
34504                 addressLine2: null,
34505                 streetName: null,
34506                 streetNumber: null,
34507                 city: null,
34508                 district: null,
34509                 state: null,
34510                 stateOrProvince: null
34511             },
34512             settings: this,
34513             domContainer: this.el.dom,
34514             geodecoder: new google.maps.Geocoder()
34515         };
34516     },
34517     
34518     drawCircle: function(center, radius, options) 
34519     {
34520         if (this.gMapContext.circle != null) {
34521             this.gMapContext.circle.setMap(null);
34522         }
34523         if (radius > 0) {
34524             radius *= 1;
34525             options = Roo.apply({}, options, {
34526                 strokeColor: "#0000FF",
34527                 strokeOpacity: .35,
34528                 strokeWeight: 2,
34529                 fillColor: "#0000FF",
34530                 fillOpacity: .2
34531             });
34532             
34533             options.map = this.gMapContext.map;
34534             options.radius = radius;
34535             options.center = center;
34536             this.gMapContext.circle = new google.maps.Circle(options);
34537             return this.gMapContext.circle;
34538         }
34539         
34540         return null;
34541     },
34542     
34543     setPosition: function(location) 
34544     {
34545         this.gMapContext.location = location;
34546         this.gMapContext.marker.setPosition(location);
34547         this.gMapContext.map.panTo(location);
34548         this.drawCircle(location, this.gMapContext.radius, {});
34549         
34550         var _this = this;
34551         
34552         if (this.gMapContext.settings.enableReverseGeocode) {
34553             this.gMapContext.geodecoder.geocode({
34554                 latLng: this.gMapContext.location
34555             }, function(results, status) {
34556                 
34557                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34558                     _this.gMapContext.locationName = results[0].formatted_address;
34559                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34560                     
34561                     _this.fireEvent('positionchanged', this, location);
34562                 }
34563             });
34564             
34565             return;
34566         }
34567         
34568         this.fireEvent('positionchanged', this, location);
34569     },
34570     
34571     resize: function()
34572     {
34573         google.maps.event.trigger(this.gMapContext.map, "resize");
34574         
34575         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
34576         
34577         this.fireEvent('resize', this);
34578     },
34579     
34580     setPositionByLatLng: function(latitude, longitude)
34581     {
34582         this.setPosition(new google.maps.LatLng(latitude, longitude));
34583     },
34584     
34585     getCurrentPosition: function() 
34586     {
34587         return {
34588             latitude: this.gMapContext.location.lat(),
34589             longitude: this.gMapContext.location.lng()
34590         };
34591     },
34592     
34593     getAddressName: function() 
34594     {
34595         return this.gMapContext.locationName;
34596     },
34597     
34598     getAddressComponents: function() 
34599     {
34600         return this.gMapContext.addressComponents;
34601     },
34602     
34603     address_component_from_google_geocode: function(address_components) 
34604     {
34605         var result = {};
34606         
34607         for (var i = 0; i < address_components.length; i++) {
34608             var component = address_components[i];
34609             if (component.types.indexOf("postal_code") >= 0) {
34610                 result.postalCode = component.short_name;
34611             } else if (component.types.indexOf("street_number") >= 0) {
34612                 result.streetNumber = component.short_name;
34613             } else if (component.types.indexOf("route") >= 0) {
34614                 result.streetName = component.short_name;
34615             } else if (component.types.indexOf("neighborhood") >= 0) {
34616                 result.city = component.short_name;
34617             } else if (component.types.indexOf("locality") >= 0) {
34618                 result.city = component.short_name;
34619             } else if (component.types.indexOf("sublocality") >= 0) {
34620                 result.district = component.short_name;
34621             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
34622                 result.stateOrProvince = component.short_name;
34623             } else if (component.types.indexOf("country") >= 0) {
34624                 result.country = component.short_name;
34625             }
34626         }
34627         
34628         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
34629         result.addressLine2 = "";
34630         return result;
34631     },
34632     
34633     setZoomLevel: function(zoom)
34634     {
34635         this.gMapContext.map.setZoom(zoom);
34636     },
34637     
34638     show: function()
34639     {
34640         if(!this.el){
34641             return;
34642         }
34643         
34644         this.el.show();
34645         
34646         this.resize();
34647         
34648         this.fireEvent('show', this);
34649     },
34650     
34651     hide: function()
34652     {
34653         if(!this.el){
34654             return;
34655         }
34656         
34657         this.el.hide();
34658         
34659         this.fireEvent('hide', this);
34660     }
34661     
34662 });
34663
34664 Roo.apply(Roo.bootstrap.LocationPicker, {
34665     
34666     OverlayView : function(map, options)
34667     {
34668         options = options || {};
34669         
34670         this.setMap(map);
34671     }
34672     
34673     
34674 });/**
34675  * @class Roo.bootstrap.Alert
34676  * @extends Roo.bootstrap.Component
34677  * Bootstrap Alert class - shows an alert area box
34678  * eg
34679  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
34680   Enter a valid email address
34681 </div>
34682  * @licence LGPL
34683  * @cfg {String} title The title of alert
34684  * @cfg {String} html The content of alert
34685  * @cfg {String} weight (success|info|warning|danger) Weight of the message
34686  * @cfg {String} fa font-awesomeicon
34687  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
34688  * @cfg {Boolean} close true to show a x closer
34689  * 
34690  * 
34691  * @constructor
34692  * Create a new alert
34693  * @param {Object} config The config object
34694  */
34695
34696
34697 Roo.bootstrap.Alert = function(config){
34698     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
34699     
34700 };
34701
34702 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
34703     
34704     title: '',
34705     html: '',
34706     weight: false,
34707     fa: false,
34708     faicon: false, // BC
34709     close : false,
34710     
34711     
34712     getAutoCreate : function()
34713     {
34714         
34715         var cfg = {
34716             tag : 'div',
34717             cls : 'alert',
34718             cn : [
34719                 {
34720                     tag: 'button',
34721                     type :  "button",
34722                     cls: "close",
34723                     html : '×',
34724                     style : this.close ? '' : 'display:none'
34725                 },
34726                 {
34727                     tag : 'i',
34728                     cls : 'roo-alert-icon'
34729                     
34730                 },
34731                 {
34732                     tag : 'b',
34733                     cls : 'roo-alert-title',
34734                     html : this.title
34735                 },
34736                 {
34737                     tag : 'span',
34738                     cls : 'roo-alert-text',
34739                     html : this.html
34740                 }
34741             ]
34742         };
34743         
34744         if(this.faicon){
34745             cfg.cn[0].cls += ' fa ' + this.faicon;
34746         }
34747         if(this.fa){
34748             cfg.cn[0].cls += ' fa ' + this.fa;
34749         }
34750         
34751         if(this.weight){
34752             cfg.cls += ' alert-' + this.weight;
34753         }
34754         
34755         return cfg;
34756     },
34757     
34758     initEvents: function() 
34759     {
34760         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34761         this.titleEl =  this.el.select('.roo-alert-title',true).first();
34762         this.iconEl = this.el.select('.roo-alert-icon',true).first();
34763         this.htmlEl = this.el.select('.roo-alert-text',true).first();
34764         if (this.seconds > 0) {
34765             this.hide.defer(this.seconds, this);
34766         }
34767     },
34768     /**
34769      * Set the Title Message HTML
34770      * @param {String} html
34771      */
34772     setTitle : function(str)
34773     {
34774         this.titleEl.dom.innerHTML = str;
34775     },
34776      
34777      /**
34778      * Set the Body Message HTML
34779      * @param {String} html
34780      */
34781     setHtml : function(str)
34782     {
34783         this.htmlEl.dom.innerHTML = str;
34784     },
34785     /**
34786      * Set the Weight of the alert
34787      * @param {String} (success|info|warning|danger) weight
34788      */
34789     
34790     setWeight : function(weight)
34791     {
34792         if(this.weight){
34793             this.el.removeClass('alert-' + this.weight);
34794         }
34795         
34796         this.weight = weight;
34797         
34798         this.el.addClass('alert-' + this.weight);
34799     },
34800       /**
34801      * Set the Icon of the alert
34802      * @param {String} see fontawsome names (name without the 'fa-' bit)
34803      */
34804     setIcon : function(icon)
34805     {
34806         if(this.faicon){
34807             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
34808         }
34809         
34810         this.faicon = icon;
34811         
34812         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
34813     },
34814     /**
34815      * Hide the Alert
34816      */
34817     hide: function() 
34818     {
34819         this.el.hide();   
34820     },
34821     /**
34822      * Show the Alert
34823      */
34824     show: function() 
34825     {  
34826         this.el.show();   
34827     }
34828     
34829 });
34830
34831  
34832 /*
34833 * Licence: LGPL
34834 */
34835
34836 /**
34837  * @class Roo.bootstrap.UploadCropbox
34838  * @extends Roo.bootstrap.Component
34839  * Bootstrap UploadCropbox class
34840  * @cfg {String} emptyText show when image has been loaded
34841  * @cfg {String} rotateNotify show when image too small to rotate
34842  * @cfg {Number} errorTimeout default 3000
34843  * @cfg {Number} minWidth default 300
34844  * @cfg {Number} minHeight default 300
34845  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
34846  * @cfg {Boolean} isDocument (true|false) default false
34847  * @cfg {String} url action url
34848  * @cfg {String} paramName default 'imageUpload'
34849  * @cfg {String} method default POST
34850  * @cfg {Boolean} loadMask (true|false) default true
34851  * @cfg {Boolean} loadingText default 'Loading...'
34852  * 
34853  * @constructor
34854  * Create a new UploadCropbox
34855  * @param {Object} config The config object
34856  */
34857
34858 Roo.bootstrap.UploadCropbox = function(config){
34859     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
34860     
34861     this.addEvents({
34862         /**
34863          * @event beforeselectfile
34864          * Fire before select file
34865          * @param {Roo.bootstrap.UploadCropbox} this
34866          */
34867         "beforeselectfile" : true,
34868         /**
34869          * @event initial
34870          * Fire after initEvent
34871          * @param {Roo.bootstrap.UploadCropbox} this
34872          */
34873         "initial" : true,
34874         /**
34875          * @event crop
34876          * Fire after initEvent
34877          * @param {Roo.bootstrap.UploadCropbox} this
34878          * @param {String} data
34879          */
34880         "crop" : true,
34881         /**
34882          * @event prepare
34883          * Fire when preparing the file data
34884          * @param {Roo.bootstrap.UploadCropbox} this
34885          * @param {Object} file
34886          */
34887         "prepare" : true,
34888         /**
34889          * @event exception
34890          * Fire when get exception
34891          * @param {Roo.bootstrap.UploadCropbox} this
34892          * @param {XMLHttpRequest} xhr
34893          */
34894         "exception" : true,
34895         /**
34896          * @event beforeloadcanvas
34897          * Fire before load the canvas
34898          * @param {Roo.bootstrap.UploadCropbox} this
34899          * @param {String} src
34900          */
34901         "beforeloadcanvas" : true,
34902         /**
34903          * @event trash
34904          * Fire when trash image
34905          * @param {Roo.bootstrap.UploadCropbox} this
34906          */
34907         "trash" : true,
34908         /**
34909          * @event download
34910          * Fire when download the image
34911          * @param {Roo.bootstrap.UploadCropbox} this
34912          */
34913         "download" : true,
34914         /**
34915          * @event footerbuttonclick
34916          * Fire when footerbuttonclick
34917          * @param {Roo.bootstrap.UploadCropbox} this
34918          * @param {String} type
34919          */
34920         "footerbuttonclick" : true,
34921         /**
34922          * @event resize
34923          * Fire when resize
34924          * @param {Roo.bootstrap.UploadCropbox} this
34925          */
34926         "resize" : true,
34927         /**
34928          * @event rotate
34929          * Fire when rotate the image
34930          * @param {Roo.bootstrap.UploadCropbox} this
34931          * @param {String} pos
34932          */
34933         "rotate" : true,
34934         /**
34935          * @event inspect
34936          * Fire when inspect the file
34937          * @param {Roo.bootstrap.UploadCropbox} this
34938          * @param {Object} file
34939          */
34940         "inspect" : true,
34941         /**
34942          * @event upload
34943          * Fire when xhr upload the file
34944          * @param {Roo.bootstrap.UploadCropbox} this
34945          * @param {Object} data
34946          */
34947         "upload" : true,
34948         /**
34949          * @event arrange
34950          * Fire when arrange the file data
34951          * @param {Roo.bootstrap.UploadCropbox} this
34952          * @param {Object} formData
34953          */
34954         "arrange" : true
34955     });
34956     
34957     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
34958 };
34959
34960 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
34961     
34962     emptyText : 'Click to upload image',
34963     rotateNotify : 'Image is too small to rotate',
34964     errorTimeout : 3000,
34965     scale : 0,
34966     baseScale : 1,
34967     rotate : 0,
34968     dragable : false,
34969     pinching : false,
34970     mouseX : 0,
34971     mouseY : 0,
34972     cropData : false,
34973     minWidth : 300,
34974     minHeight : 300,
34975     file : false,
34976     exif : {},
34977     baseRotate : 1,
34978     cropType : 'image/jpeg',
34979     buttons : false,
34980     canvasLoaded : false,
34981     isDocument : false,
34982     method : 'POST',
34983     paramName : 'imageUpload',
34984     loadMask : true,
34985     loadingText : 'Loading...',
34986     maskEl : false,
34987     
34988     getAutoCreate : function()
34989     {
34990         var cfg = {
34991             tag : 'div',
34992             cls : 'roo-upload-cropbox',
34993             cn : [
34994                 {
34995                     tag : 'input',
34996                     cls : 'roo-upload-cropbox-selector',
34997                     type : 'file'
34998                 },
34999                 {
35000                     tag : 'div',
35001                     cls : 'roo-upload-cropbox-body',
35002                     style : 'cursor:pointer',
35003                     cn : [
35004                         {
35005                             tag : 'div',
35006                             cls : 'roo-upload-cropbox-preview'
35007                         },
35008                         {
35009                             tag : 'div',
35010                             cls : 'roo-upload-cropbox-thumb'
35011                         },
35012                         {
35013                             tag : 'div',
35014                             cls : 'roo-upload-cropbox-empty-notify',
35015                             html : this.emptyText
35016                         },
35017                         {
35018                             tag : 'div',
35019                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35020                             html : this.rotateNotify
35021                         }
35022                     ]
35023                 },
35024                 {
35025                     tag : 'div',
35026                     cls : 'roo-upload-cropbox-footer',
35027                     cn : {
35028                         tag : 'div',
35029                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35030                         cn : []
35031                     }
35032                 }
35033             ]
35034         };
35035         
35036         return cfg;
35037     },
35038     
35039     onRender : function(ct, position)
35040     {
35041         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35042         
35043         if (this.buttons.length) {
35044             
35045             Roo.each(this.buttons, function(bb) {
35046                 
35047                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35048                 
35049                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35050                 
35051             }, this);
35052         }
35053         
35054         if(this.loadMask){
35055             this.maskEl = this.el;
35056         }
35057     },
35058     
35059     initEvents : function()
35060     {
35061         this.urlAPI = (window.createObjectURL && window) || 
35062                                 (window.URL && URL.revokeObjectURL && URL) || 
35063                                 (window.webkitURL && webkitURL);
35064                         
35065         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35066         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35067         
35068         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35069         this.selectorEl.hide();
35070         
35071         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35072         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35073         
35074         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35075         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35076         this.thumbEl.hide();
35077         
35078         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35079         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35080         
35081         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35082         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35083         this.errorEl.hide();
35084         
35085         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35086         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35087         this.footerEl.hide();
35088         
35089         this.setThumbBoxSize();
35090         
35091         this.bind();
35092         
35093         this.resize();
35094         
35095         this.fireEvent('initial', this);
35096     },
35097
35098     bind : function()
35099     {
35100         var _this = this;
35101         
35102         window.addEventListener("resize", function() { _this.resize(); } );
35103         
35104         this.bodyEl.on('click', this.beforeSelectFile, this);
35105         
35106         if(Roo.isTouch){
35107             this.bodyEl.on('touchstart', this.onTouchStart, this);
35108             this.bodyEl.on('touchmove', this.onTouchMove, this);
35109             this.bodyEl.on('touchend', this.onTouchEnd, this);
35110         }
35111         
35112         if(!Roo.isTouch){
35113             this.bodyEl.on('mousedown', this.onMouseDown, this);
35114             this.bodyEl.on('mousemove', this.onMouseMove, this);
35115             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35116             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35117             Roo.get(document).on('mouseup', this.onMouseUp, this);
35118         }
35119         
35120         this.selectorEl.on('change', this.onFileSelected, this);
35121     },
35122     
35123     reset : function()
35124     {    
35125         this.scale = 0;
35126         this.baseScale = 1;
35127         this.rotate = 0;
35128         this.baseRotate = 1;
35129         this.dragable = false;
35130         this.pinching = false;
35131         this.mouseX = 0;
35132         this.mouseY = 0;
35133         this.cropData = false;
35134         this.notifyEl.dom.innerHTML = this.emptyText;
35135         
35136         this.selectorEl.dom.value = '';
35137         
35138     },
35139     
35140     resize : function()
35141     {
35142         if(this.fireEvent('resize', this) != false){
35143             this.setThumbBoxPosition();
35144             this.setCanvasPosition();
35145         }
35146     },
35147     
35148     onFooterButtonClick : function(e, el, o, type)
35149     {
35150         switch (type) {
35151             case 'rotate-left' :
35152                 this.onRotateLeft(e);
35153                 break;
35154             case 'rotate-right' :
35155                 this.onRotateRight(e);
35156                 break;
35157             case 'picture' :
35158                 this.beforeSelectFile(e);
35159                 break;
35160             case 'trash' :
35161                 this.trash(e);
35162                 break;
35163             case 'crop' :
35164                 this.crop(e);
35165                 break;
35166             case 'download' :
35167                 this.download(e);
35168                 break;
35169             default :
35170                 break;
35171         }
35172         
35173         this.fireEvent('footerbuttonclick', this, type);
35174     },
35175     
35176     beforeSelectFile : function(e)
35177     {
35178         e.preventDefault();
35179         
35180         if(this.fireEvent('beforeselectfile', this) != false){
35181             this.selectorEl.dom.click();
35182         }
35183     },
35184     
35185     onFileSelected : function(e)
35186     {
35187         e.preventDefault();
35188         
35189         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35190             return;
35191         }
35192         
35193         var file = this.selectorEl.dom.files[0];
35194         
35195         if(this.fireEvent('inspect', this, file) != false){
35196             this.prepare(file);
35197         }
35198         
35199     },
35200     
35201     trash : function(e)
35202     {
35203         this.fireEvent('trash', this);
35204     },
35205     
35206     download : function(e)
35207     {
35208         this.fireEvent('download', this);
35209     },
35210     
35211     loadCanvas : function(src)
35212     {   
35213         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35214             
35215             this.reset();
35216             
35217             this.imageEl = document.createElement('img');
35218             
35219             var _this = this;
35220             
35221             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35222             
35223             this.imageEl.src = src;
35224         }
35225     },
35226     
35227     onLoadCanvas : function()
35228     {   
35229         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35230         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35231         
35232         this.bodyEl.un('click', this.beforeSelectFile, this);
35233         
35234         this.notifyEl.hide();
35235         this.thumbEl.show();
35236         this.footerEl.show();
35237         
35238         this.baseRotateLevel();
35239         
35240         if(this.isDocument){
35241             this.setThumbBoxSize();
35242         }
35243         
35244         this.setThumbBoxPosition();
35245         
35246         this.baseScaleLevel();
35247         
35248         this.draw();
35249         
35250         this.resize();
35251         
35252         this.canvasLoaded = true;
35253         
35254         if(this.loadMask){
35255             this.maskEl.unmask();
35256         }
35257         
35258     },
35259     
35260     setCanvasPosition : function()
35261     {   
35262         if(!this.canvasEl){
35263             return;
35264         }
35265         
35266         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35267         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35268         
35269         this.previewEl.setLeft(pw);
35270         this.previewEl.setTop(ph);
35271         
35272     },
35273     
35274     onMouseDown : function(e)
35275     {   
35276         e.stopEvent();
35277         
35278         this.dragable = true;
35279         this.pinching = false;
35280         
35281         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35282             this.dragable = false;
35283             return;
35284         }
35285         
35286         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35287         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35288         
35289     },
35290     
35291     onMouseMove : function(e)
35292     {   
35293         e.stopEvent();
35294         
35295         if(!this.canvasLoaded){
35296             return;
35297         }
35298         
35299         if (!this.dragable){
35300             return;
35301         }
35302         
35303         var minX = Math.ceil(this.thumbEl.getLeft(true));
35304         var minY = Math.ceil(this.thumbEl.getTop(true));
35305         
35306         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35307         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35308         
35309         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35310         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35311         
35312         x = x - this.mouseX;
35313         y = y - this.mouseY;
35314         
35315         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35316         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35317         
35318         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35319         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35320         
35321         this.previewEl.setLeft(bgX);
35322         this.previewEl.setTop(bgY);
35323         
35324         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35325         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35326     },
35327     
35328     onMouseUp : function(e)
35329     {   
35330         e.stopEvent();
35331         
35332         this.dragable = false;
35333     },
35334     
35335     onMouseWheel : function(e)
35336     {   
35337         e.stopEvent();
35338         
35339         this.startScale = this.scale;
35340         
35341         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35342         
35343         if(!this.zoomable()){
35344             this.scale = this.startScale;
35345             return;
35346         }
35347         
35348         this.draw();
35349         
35350         return;
35351     },
35352     
35353     zoomable : function()
35354     {
35355         var minScale = this.thumbEl.getWidth() / this.minWidth;
35356         
35357         if(this.minWidth < this.minHeight){
35358             minScale = this.thumbEl.getHeight() / this.minHeight;
35359         }
35360         
35361         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35362         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35363         
35364         if(
35365                 this.isDocument &&
35366                 (this.rotate == 0 || this.rotate == 180) && 
35367                 (
35368                     width > this.imageEl.OriginWidth || 
35369                     height > this.imageEl.OriginHeight ||
35370                     (width < this.minWidth && height < this.minHeight)
35371                 )
35372         ){
35373             return false;
35374         }
35375         
35376         if(
35377                 this.isDocument &&
35378                 (this.rotate == 90 || this.rotate == 270) && 
35379                 (
35380                     width > this.imageEl.OriginWidth || 
35381                     height > this.imageEl.OriginHeight ||
35382                     (width < this.minHeight && height < this.minWidth)
35383                 )
35384         ){
35385             return false;
35386         }
35387         
35388         if(
35389                 !this.isDocument &&
35390                 (this.rotate == 0 || this.rotate == 180) && 
35391                 (
35392                     width < this.minWidth || 
35393                     width > this.imageEl.OriginWidth || 
35394                     height < this.minHeight || 
35395                     height > this.imageEl.OriginHeight
35396                 )
35397         ){
35398             return false;
35399         }
35400         
35401         if(
35402                 !this.isDocument &&
35403                 (this.rotate == 90 || this.rotate == 270) && 
35404                 (
35405                     width < this.minHeight || 
35406                     width > this.imageEl.OriginWidth || 
35407                     height < this.minWidth || 
35408                     height > this.imageEl.OriginHeight
35409                 )
35410         ){
35411             return false;
35412         }
35413         
35414         return true;
35415         
35416     },
35417     
35418     onRotateLeft : function(e)
35419     {   
35420         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35421             
35422             var minScale = this.thumbEl.getWidth() / this.minWidth;
35423             
35424             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35425             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35426             
35427             this.startScale = this.scale;
35428             
35429             while (this.getScaleLevel() < minScale){
35430             
35431                 this.scale = this.scale + 1;
35432                 
35433                 if(!this.zoomable()){
35434                     break;
35435                 }
35436                 
35437                 if(
35438                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35439                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35440                 ){
35441                     continue;
35442                 }
35443                 
35444                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35445
35446                 this.draw();
35447                 
35448                 return;
35449             }
35450             
35451             this.scale = this.startScale;
35452             
35453             this.onRotateFail();
35454             
35455             return false;
35456         }
35457         
35458         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35459
35460         if(this.isDocument){
35461             this.setThumbBoxSize();
35462             this.setThumbBoxPosition();
35463             this.setCanvasPosition();
35464         }
35465         
35466         this.draw();
35467         
35468         this.fireEvent('rotate', this, 'left');
35469         
35470     },
35471     
35472     onRotateRight : function(e)
35473     {
35474         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35475             
35476             var minScale = this.thumbEl.getWidth() / this.minWidth;
35477         
35478             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35479             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35480             
35481             this.startScale = this.scale;
35482             
35483             while (this.getScaleLevel() < minScale){
35484             
35485                 this.scale = this.scale + 1;
35486                 
35487                 if(!this.zoomable()){
35488                     break;
35489                 }
35490                 
35491                 if(
35492                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35493                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35494                 ){
35495                     continue;
35496                 }
35497                 
35498                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35499
35500                 this.draw();
35501                 
35502                 return;
35503             }
35504             
35505             this.scale = this.startScale;
35506             
35507             this.onRotateFail();
35508             
35509             return false;
35510         }
35511         
35512         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35513
35514         if(this.isDocument){
35515             this.setThumbBoxSize();
35516             this.setThumbBoxPosition();
35517             this.setCanvasPosition();
35518         }
35519         
35520         this.draw();
35521         
35522         this.fireEvent('rotate', this, 'right');
35523     },
35524     
35525     onRotateFail : function()
35526     {
35527         this.errorEl.show(true);
35528         
35529         var _this = this;
35530         
35531         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35532     },
35533     
35534     draw : function()
35535     {
35536         this.previewEl.dom.innerHTML = '';
35537         
35538         var canvasEl = document.createElement("canvas");
35539         
35540         var contextEl = canvasEl.getContext("2d");
35541         
35542         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35543         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35544         var center = this.imageEl.OriginWidth / 2;
35545         
35546         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35547             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35548             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35549             center = this.imageEl.OriginHeight / 2;
35550         }
35551         
35552         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35553         
35554         contextEl.translate(center, center);
35555         contextEl.rotate(this.rotate * Math.PI / 180);
35556
35557         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35558         
35559         this.canvasEl = document.createElement("canvas");
35560         
35561         this.contextEl = this.canvasEl.getContext("2d");
35562         
35563         switch (this.rotate) {
35564             case 0 :
35565                 
35566                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35567                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35568                 
35569                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35570                 
35571                 break;
35572             case 90 : 
35573                 
35574                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35575                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35576                 
35577                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35578                     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);
35579                     break;
35580                 }
35581                 
35582                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35583                 
35584                 break;
35585             case 180 :
35586                 
35587                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35588                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35589                 
35590                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35591                     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);
35592                     break;
35593                 }
35594                 
35595                 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);
35596                 
35597                 break;
35598             case 270 :
35599                 
35600                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35601                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35602         
35603                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35604                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35605                     break;
35606                 }
35607                 
35608                 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);
35609                 
35610                 break;
35611             default : 
35612                 break;
35613         }
35614         
35615         this.previewEl.appendChild(this.canvasEl);
35616         
35617         this.setCanvasPosition();
35618     },
35619     
35620     crop : function()
35621     {
35622         if(!this.canvasLoaded){
35623             return;
35624         }
35625         
35626         var imageCanvas = document.createElement("canvas");
35627         
35628         var imageContext = imageCanvas.getContext("2d");
35629         
35630         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35631         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35632         
35633         var center = imageCanvas.width / 2;
35634         
35635         imageContext.translate(center, center);
35636         
35637         imageContext.rotate(this.rotate * Math.PI / 180);
35638         
35639         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35640         
35641         var canvas = document.createElement("canvas");
35642         
35643         var context = canvas.getContext("2d");
35644                 
35645         canvas.width = this.minWidth;
35646         canvas.height = this.minHeight;
35647
35648         switch (this.rotate) {
35649             case 0 :
35650                 
35651                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35652                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35653                 
35654                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35655                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35656                 
35657                 var targetWidth = this.minWidth - 2 * x;
35658                 var targetHeight = this.minHeight - 2 * y;
35659                 
35660                 var scale = 1;
35661                 
35662                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35663                     scale = targetWidth / width;
35664                 }
35665                 
35666                 if(x > 0 && y == 0){
35667                     scale = targetHeight / height;
35668                 }
35669                 
35670                 if(x > 0 && y > 0){
35671                     scale = targetWidth / width;
35672                     
35673                     if(width < height){
35674                         scale = targetHeight / height;
35675                     }
35676                 }
35677                 
35678                 context.scale(scale, scale);
35679                 
35680                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35681                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35682
35683                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35684                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35685
35686                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35687                 
35688                 break;
35689             case 90 : 
35690                 
35691                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35692                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35693                 
35694                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35695                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35696                 
35697                 var targetWidth = this.minWidth - 2 * x;
35698                 var targetHeight = this.minHeight - 2 * y;
35699                 
35700                 var scale = 1;
35701                 
35702                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35703                     scale = targetWidth / width;
35704                 }
35705                 
35706                 if(x > 0 && y == 0){
35707                     scale = targetHeight / height;
35708                 }
35709                 
35710                 if(x > 0 && y > 0){
35711                     scale = targetWidth / width;
35712                     
35713                     if(width < height){
35714                         scale = targetHeight / height;
35715                     }
35716                 }
35717                 
35718                 context.scale(scale, scale);
35719                 
35720                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35721                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35722
35723                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35724                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35725                 
35726                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35727                 
35728                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35729                 
35730                 break;
35731             case 180 :
35732                 
35733                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35734                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35735                 
35736                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35737                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35738                 
35739                 var targetWidth = this.minWidth - 2 * x;
35740                 var targetHeight = this.minHeight - 2 * y;
35741                 
35742                 var scale = 1;
35743                 
35744                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35745                     scale = targetWidth / width;
35746                 }
35747                 
35748                 if(x > 0 && y == 0){
35749                     scale = targetHeight / height;
35750                 }
35751                 
35752                 if(x > 0 && y > 0){
35753                     scale = targetWidth / width;
35754                     
35755                     if(width < height){
35756                         scale = targetHeight / height;
35757                     }
35758                 }
35759                 
35760                 context.scale(scale, scale);
35761                 
35762                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35763                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35764
35765                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35766                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35767
35768                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35769                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35770                 
35771                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35772                 
35773                 break;
35774             case 270 :
35775                 
35776                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35777                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35778                 
35779                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35780                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35781                 
35782                 var targetWidth = this.minWidth - 2 * x;
35783                 var targetHeight = this.minHeight - 2 * y;
35784                 
35785                 var scale = 1;
35786                 
35787                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35788                     scale = targetWidth / width;
35789                 }
35790                 
35791                 if(x > 0 && y == 0){
35792                     scale = targetHeight / height;
35793                 }
35794                 
35795                 if(x > 0 && y > 0){
35796                     scale = targetWidth / width;
35797                     
35798                     if(width < height){
35799                         scale = targetHeight / height;
35800                     }
35801                 }
35802                 
35803                 context.scale(scale, scale);
35804                 
35805                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35806                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35807
35808                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35809                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35810                 
35811                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35812                 
35813                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35814                 
35815                 break;
35816             default : 
35817                 break;
35818         }
35819         
35820         this.cropData = canvas.toDataURL(this.cropType);
35821         
35822         if(this.fireEvent('crop', this, this.cropData) !== false){
35823             this.process(this.file, this.cropData);
35824         }
35825         
35826         return;
35827         
35828     },
35829     
35830     setThumbBoxSize : function()
35831     {
35832         var width, height;
35833         
35834         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
35835             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
35836             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
35837             
35838             this.minWidth = width;
35839             this.minHeight = height;
35840             
35841             if(this.rotate == 90 || this.rotate == 270){
35842                 this.minWidth = height;
35843                 this.minHeight = width;
35844             }
35845         }
35846         
35847         height = 300;
35848         width = Math.ceil(this.minWidth * height / this.minHeight);
35849         
35850         if(this.minWidth > this.minHeight){
35851             width = 300;
35852             height = Math.ceil(this.minHeight * width / this.minWidth);
35853         }
35854         
35855         this.thumbEl.setStyle({
35856             width : width + 'px',
35857             height : height + 'px'
35858         });
35859
35860         return;
35861             
35862     },
35863     
35864     setThumbBoxPosition : function()
35865     {
35866         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
35867         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
35868         
35869         this.thumbEl.setLeft(x);
35870         this.thumbEl.setTop(y);
35871         
35872     },
35873     
35874     baseRotateLevel : function()
35875     {
35876         this.baseRotate = 1;
35877         
35878         if(
35879                 typeof(this.exif) != 'undefined' &&
35880                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
35881                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
35882         ){
35883             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
35884         }
35885         
35886         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
35887         
35888     },
35889     
35890     baseScaleLevel : function()
35891     {
35892         var width, height;
35893         
35894         if(this.isDocument){
35895             
35896             if(this.baseRotate == 6 || this.baseRotate == 8){
35897             
35898                 height = this.thumbEl.getHeight();
35899                 this.baseScale = height / this.imageEl.OriginWidth;
35900
35901                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
35902                     width = this.thumbEl.getWidth();
35903                     this.baseScale = width / this.imageEl.OriginHeight;
35904                 }
35905
35906                 return;
35907             }
35908
35909             height = this.thumbEl.getHeight();
35910             this.baseScale = height / this.imageEl.OriginHeight;
35911
35912             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
35913                 width = this.thumbEl.getWidth();
35914                 this.baseScale = width / this.imageEl.OriginWidth;
35915             }
35916
35917             return;
35918         }
35919         
35920         if(this.baseRotate == 6 || this.baseRotate == 8){
35921             
35922             width = this.thumbEl.getHeight();
35923             this.baseScale = width / this.imageEl.OriginHeight;
35924             
35925             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
35926                 height = this.thumbEl.getWidth();
35927                 this.baseScale = height / this.imageEl.OriginHeight;
35928             }
35929             
35930             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35931                 height = this.thumbEl.getWidth();
35932                 this.baseScale = height / this.imageEl.OriginHeight;
35933                 
35934                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
35935                     width = this.thumbEl.getHeight();
35936                     this.baseScale = width / this.imageEl.OriginWidth;
35937                 }
35938             }
35939             
35940             return;
35941         }
35942         
35943         width = this.thumbEl.getWidth();
35944         this.baseScale = width / this.imageEl.OriginWidth;
35945         
35946         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
35947             height = this.thumbEl.getHeight();
35948             this.baseScale = height / this.imageEl.OriginHeight;
35949         }
35950         
35951         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35952             
35953             height = this.thumbEl.getHeight();
35954             this.baseScale = height / this.imageEl.OriginHeight;
35955             
35956             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
35957                 width = this.thumbEl.getWidth();
35958                 this.baseScale = width / this.imageEl.OriginWidth;
35959             }
35960             
35961         }
35962         
35963         return;
35964     },
35965     
35966     getScaleLevel : function()
35967     {
35968         return this.baseScale * Math.pow(1.1, this.scale);
35969     },
35970     
35971     onTouchStart : function(e)
35972     {
35973         if(!this.canvasLoaded){
35974             this.beforeSelectFile(e);
35975             return;
35976         }
35977         
35978         var touches = e.browserEvent.touches;
35979         
35980         if(!touches){
35981             return;
35982         }
35983         
35984         if(touches.length == 1){
35985             this.onMouseDown(e);
35986             return;
35987         }
35988         
35989         if(touches.length != 2){
35990             return;
35991         }
35992         
35993         var coords = [];
35994         
35995         for(var i = 0, finger; finger = touches[i]; i++){
35996             coords.push(finger.pageX, finger.pageY);
35997         }
35998         
35999         var x = Math.pow(coords[0] - coords[2], 2);
36000         var y = Math.pow(coords[1] - coords[3], 2);
36001         
36002         this.startDistance = Math.sqrt(x + y);
36003         
36004         this.startScale = this.scale;
36005         
36006         this.pinching = true;
36007         this.dragable = false;
36008         
36009     },
36010     
36011     onTouchMove : function(e)
36012     {
36013         if(!this.pinching && !this.dragable){
36014             return;
36015         }
36016         
36017         var touches = e.browserEvent.touches;
36018         
36019         if(!touches){
36020             return;
36021         }
36022         
36023         if(this.dragable){
36024             this.onMouseMove(e);
36025             return;
36026         }
36027         
36028         var coords = [];
36029         
36030         for(var i = 0, finger; finger = touches[i]; i++){
36031             coords.push(finger.pageX, finger.pageY);
36032         }
36033         
36034         var x = Math.pow(coords[0] - coords[2], 2);
36035         var y = Math.pow(coords[1] - coords[3], 2);
36036         
36037         this.endDistance = Math.sqrt(x + y);
36038         
36039         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36040         
36041         if(!this.zoomable()){
36042             this.scale = this.startScale;
36043             return;
36044         }
36045         
36046         this.draw();
36047         
36048     },
36049     
36050     onTouchEnd : function(e)
36051     {
36052         this.pinching = false;
36053         this.dragable = false;
36054         
36055     },
36056     
36057     process : function(file, crop)
36058     {
36059         if(this.loadMask){
36060             this.maskEl.mask(this.loadingText);
36061         }
36062         
36063         this.xhr = new XMLHttpRequest();
36064         
36065         file.xhr = this.xhr;
36066
36067         this.xhr.open(this.method, this.url, true);
36068         
36069         var headers = {
36070             "Accept": "application/json",
36071             "Cache-Control": "no-cache",
36072             "X-Requested-With": "XMLHttpRequest"
36073         };
36074         
36075         for (var headerName in headers) {
36076             var headerValue = headers[headerName];
36077             if (headerValue) {
36078                 this.xhr.setRequestHeader(headerName, headerValue);
36079             }
36080         }
36081         
36082         var _this = this;
36083         
36084         this.xhr.onload = function()
36085         {
36086             _this.xhrOnLoad(_this.xhr);
36087         }
36088         
36089         this.xhr.onerror = function()
36090         {
36091             _this.xhrOnError(_this.xhr);
36092         }
36093         
36094         var formData = new FormData();
36095
36096         formData.append('returnHTML', 'NO');
36097         
36098         if(crop){
36099             formData.append('crop', crop);
36100         }
36101         
36102         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36103             formData.append(this.paramName, file, file.name);
36104         }
36105         
36106         if(typeof(file.filename) != 'undefined'){
36107             formData.append('filename', file.filename);
36108         }
36109         
36110         if(typeof(file.mimetype) != 'undefined'){
36111             formData.append('mimetype', file.mimetype);
36112         }
36113         
36114         if(this.fireEvent('arrange', this, formData) != false){
36115             this.xhr.send(formData);
36116         };
36117     },
36118     
36119     xhrOnLoad : function(xhr)
36120     {
36121         if(this.loadMask){
36122             this.maskEl.unmask();
36123         }
36124         
36125         if (xhr.readyState !== 4) {
36126             this.fireEvent('exception', this, xhr);
36127             return;
36128         }
36129
36130         var response = Roo.decode(xhr.responseText);
36131         
36132         if(!response.success){
36133             this.fireEvent('exception', this, xhr);
36134             return;
36135         }
36136         
36137         var response = Roo.decode(xhr.responseText);
36138         
36139         this.fireEvent('upload', this, response);
36140         
36141     },
36142     
36143     xhrOnError : function()
36144     {
36145         if(this.loadMask){
36146             this.maskEl.unmask();
36147         }
36148         
36149         Roo.log('xhr on error');
36150         
36151         var response = Roo.decode(xhr.responseText);
36152           
36153         Roo.log(response);
36154         
36155     },
36156     
36157     prepare : function(file)
36158     {   
36159         if(this.loadMask){
36160             this.maskEl.mask(this.loadingText);
36161         }
36162         
36163         this.file = false;
36164         this.exif = {};
36165         
36166         if(typeof(file) === 'string'){
36167             this.loadCanvas(file);
36168             return;
36169         }
36170         
36171         if(!file || !this.urlAPI){
36172             return;
36173         }
36174         
36175         this.file = file;
36176         this.cropType = file.type;
36177         
36178         var _this = this;
36179         
36180         if(this.fireEvent('prepare', this, this.file) != false){
36181             
36182             var reader = new FileReader();
36183             
36184             reader.onload = function (e) {
36185                 if (e.target.error) {
36186                     Roo.log(e.target.error);
36187                     return;
36188                 }
36189                 
36190                 var buffer = e.target.result,
36191                     dataView = new DataView(buffer),
36192                     offset = 2,
36193                     maxOffset = dataView.byteLength - 4,
36194                     markerBytes,
36195                     markerLength;
36196                 
36197                 if (dataView.getUint16(0) === 0xffd8) {
36198                     while (offset < maxOffset) {
36199                         markerBytes = dataView.getUint16(offset);
36200                         
36201                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36202                             markerLength = dataView.getUint16(offset + 2) + 2;
36203                             if (offset + markerLength > dataView.byteLength) {
36204                                 Roo.log('Invalid meta data: Invalid segment size.');
36205                                 break;
36206                             }
36207                             
36208                             if(markerBytes == 0xffe1){
36209                                 _this.parseExifData(
36210                                     dataView,
36211                                     offset,
36212                                     markerLength
36213                                 );
36214                             }
36215                             
36216                             offset += markerLength;
36217                             
36218                             continue;
36219                         }
36220                         
36221                         break;
36222                     }
36223                     
36224                 }
36225                 
36226                 var url = _this.urlAPI.createObjectURL(_this.file);
36227                 
36228                 _this.loadCanvas(url);
36229                 
36230                 return;
36231             }
36232             
36233             reader.readAsArrayBuffer(this.file);
36234             
36235         }
36236         
36237     },
36238     
36239     parseExifData : function(dataView, offset, length)
36240     {
36241         var tiffOffset = offset + 10,
36242             littleEndian,
36243             dirOffset;
36244     
36245         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36246             // No Exif data, might be XMP data instead
36247             return;
36248         }
36249         
36250         // Check for the ASCII code for "Exif" (0x45786966):
36251         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36252             // No Exif data, might be XMP data instead
36253             return;
36254         }
36255         if (tiffOffset + 8 > dataView.byteLength) {
36256             Roo.log('Invalid Exif data: Invalid segment size.');
36257             return;
36258         }
36259         // Check for the two null bytes:
36260         if (dataView.getUint16(offset + 8) !== 0x0000) {
36261             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36262             return;
36263         }
36264         // Check the byte alignment:
36265         switch (dataView.getUint16(tiffOffset)) {
36266         case 0x4949:
36267             littleEndian = true;
36268             break;
36269         case 0x4D4D:
36270             littleEndian = false;
36271             break;
36272         default:
36273             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36274             return;
36275         }
36276         // Check for the TIFF tag marker (0x002A):
36277         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36278             Roo.log('Invalid Exif data: Missing TIFF marker.');
36279             return;
36280         }
36281         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36282         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36283         
36284         this.parseExifTags(
36285             dataView,
36286             tiffOffset,
36287             tiffOffset + dirOffset,
36288             littleEndian
36289         );
36290     },
36291     
36292     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36293     {
36294         var tagsNumber,
36295             dirEndOffset,
36296             i;
36297         if (dirOffset + 6 > dataView.byteLength) {
36298             Roo.log('Invalid Exif data: Invalid directory offset.');
36299             return;
36300         }
36301         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36302         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36303         if (dirEndOffset + 4 > dataView.byteLength) {
36304             Roo.log('Invalid Exif data: Invalid directory size.');
36305             return;
36306         }
36307         for (i = 0; i < tagsNumber; i += 1) {
36308             this.parseExifTag(
36309                 dataView,
36310                 tiffOffset,
36311                 dirOffset + 2 + 12 * i, // tag offset
36312                 littleEndian
36313             );
36314         }
36315         // Return the offset to the next directory:
36316         return dataView.getUint32(dirEndOffset, littleEndian);
36317     },
36318     
36319     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36320     {
36321         var tag = dataView.getUint16(offset, littleEndian);
36322         
36323         this.exif[tag] = this.getExifValue(
36324             dataView,
36325             tiffOffset,
36326             offset,
36327             dataView.getUint16(offset + 2, littleEndian), // tag type
36328             dataView.getUint32(offset + 4, littleEndian), // tag length
36329             littleEndian
36330         );
36331     },
36332     
36333     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36334     {
36335         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36336             tagSize,
36337             dataOffset,
36338             values,
36339             i,
36340             str,
36341             c;
36342     
36343         if (!tagType) {
36344             Roo.log('Invalid Exif data: Invalid tag type.');
36345             return;
36346         }
36347         
36348         tagSize = tagType.size * length;
36349         // Determine if the value is contained in the dataOffset bytes,
36350         // or if the value at the dataOffset is a pointer to the actual data:
36351         dataOffset = tagSize > 4 ?
36352                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36353         if (dataOffset + tagSize > dataView.byteLength) {
36354             Roo.log('Invalid Exif data: Invalid data offset.');
36355             return;
36356         }
36357         if (length === 1) {
36358             return tagType.getValue(dataView, dataOffset, littleEndian);
36359         }
36360         values = [];
36361         for (i = 0; i < length; i += 1) {
36362             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36363         }
36364         
36365         if (tagType.ascii) {
36366             str = '';
36367             // Concatenate the chars:
36368             for (i = 0; i < values.length; i += 1) {
36369                 c = values[i];
36370                 // Ignore the terminating NULL byte(s):
36371                 if (c === '\u0000') {
36372                     break;
36373                 }
36374                 str += c;
36375             }
36376             return str;
36377         }
36378         return values;
36379     }
36380     
36381 });
36382
36383 Roo.apply(Roo.bootstrap.UploadCropbox, {
36384     tags : {
36385         'Orientation': 0x0112
36386     },
36387     
36388     Orientation: {
36389             1: 0, //'top-left',
36390 //            2: 'top-right',
36391             3: 180, //'bottom-right',
36392 //            4: 'bottom-left',
36393 //            5: 'left-top',
36394             6: 90, //'right-top',
36395 //            7: 'right-bottom',
36396             8: 270 //'left-bottom'
36397     },
36398     
36399     exifTagTypes : {
36400         // byte, 8-bit unsigned int:
36401         1: {
36402             getValue: function (dataView, dataOffset) {
36403                 return dataView.getUint8(dataOffset);
36404             },
36405             size: 1
36406         },
36407         // ascii, 8-bit byte:
36408         2: {
36409             getValue: function (dataView, dataOffset) {
36410                 return String.fromCharCode(dataView.getUint8(dataOffset));
36411             },
36412             size: 1,
36413             ascii: true
36414         },
36415         // short, 16 bit int:
36416         3: {
36417             getValue: function (dataView, dataOffset, littleEndian) {
36418                 return dataView.getUint16(dataOffset, littleEndian);
36419             },
36420             size: 2
36421         },
36422         // long, 32 bit int:
36423         4: {
36424             getValue: function (dataView, dataOffset, littleEndian) {
36425                 return dataView.getUint32(dataOffset, littleEndian);
36426             },
36427             size: 4
36428         },
36429         // rational = two long values, first is numerator, second is denominator:
36430         5: {
36431             getValue: function (dataView, dataOffset, littleEndian) {
36432                 return dataView.getUint32(dataOffset, littleEndian) /
36433                     dataView.getUint32(dataOffset + 4, littleEndian);
36434             },
36435             size: 8
36436         },
36437         // slong, 32 bit signed int:
36438         9: {
36439             getValue: function (dataView, dataOffset, littleEndian) {
36440                 return dataView.getInt32(dataOffset, littleEndian);
36441             },
36442             size: 4
36443         },
36444         // srational, two slongs, first is numerator, second is denominator:
36445         10: {
36446             getValue: function (dataView, dataOffset, littleEndian) {
36447                 return dataView.getInt32(dataOffset, littleEndian) /
36448                     dataView.getInt32(dataOffset + 4, littleEndian);
36449             },
36450             size: 8
36451         }
36452     },
36453     
36454     footer : {
36455         STANDARD : [
36456             {
36457                 tag : 'div',
36458                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36459                 action : 'rotate-left',
36460                 cn : [
36461                     {
36462                         tag : 'button',
36463                         cls : 'btn btn-default',
36464                         html : '<i class="fa fa-undo"></i>'
36465                     }
36466                 ]
36467             },
36468             {
36469                 tag : 'div',
36470                 cls : 'btn-group roo-upload-cropbox-picture',
36471                 action : 'picture',
36472                 cn : [
36473                     {
36474                         tag : 'button',
36475                         cls : 'btn btn-default',
36476                         html : '<i class="fa fa-picture-o"></i>'
36477                     }
36478                 ]
36479             },
36480             {
36481                 tag : 'div',
36482                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36483                 action : 'rotate-right',
36484                 cn : [
36485                     {
36486                         tag : 'button',
36487                         cls : 'btn btn-default',
36488                         html : '<i class="fa fa-repeat"></i>'
36489                     }
36490                 ]
36491             }
36492         ],
36493         DOCUMENT : [
36494             {
36495                 tag : 'div',
36496                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36497                 action : 'rotate-left',
36498                 cn : [
36499                     {
36500                         tag : 'button',
36501                         cls : 'btn btn-default',
36502                         html : '<i class="fa fa-undo"></i>'
36503                     }
36504                 ]
36505             },
36506             {
36507                 tag : 'div',
36508                 cls : 'btn-group roo-upload-cropbox-download',
36509                 action : 'download',
36510                 cn : [
36511                     {
36512                         tag : 'button',
36513                         cls : 'btn btn-default',
36514                         html : '<i class="fa fa-download"></i>'
36515                     }
36516                 ]
36517             },
36518             {
36519                 tag : 'div',
36520                 cls : 'btn-group roo-upload-cropbox-crop',
36521                 action : 'crop',
36522                 cn : [
36523                     {
36524                         tag : 'button',
36525                         cls : 'btn btn-default',
36526                         html : '<i class="fa fa-crop"></i>'
36527                     }
36528                 ]
36529             },
36530             {
36531                 tag : 'div',
36532                 cls : 'btn-group roo-upload-cropbox-trash',
36533                 action : 'trash',
36534                 cn : [
36535                     {
36536                         tag : 'button',
36537                         cls : 'btn btn-default',
36538                         html : '<i class="fa fa-trash"></i>'
36539                     }
36540                 ]
36541             },
36542             {
36543                 tag : 'div',
36544                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36545                 action : 'rotate-right',
36546                 cn : [
36547                     {
36548                         tag : 'button',
36549                         cls : 'btn btn-default',
36550                         html : '<i class="fa fa-repeat"></i>'
36551                     }
36552                 ]
36553             }
36554         ],
36555         ROTATOR : [
36556             {
36557                 tag : 'div',
36558                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36559                 action : 'rotate-left',
36560                 cn : [
36561                     {
36562                         tag : 'button',
36563                         cls : 'btn btn-default',
36564                         html : '<i class="fa fa-undo"></i>'
36565                     }
36566                 ]
36567             },
36568             {
36569                 tag : 'div',
36570                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36571                 action : 'rotate-right',
36572                 cn : [
36573                     {
36574                         tag : 'button',
36575                         cls : 'btn btn-default',
36576                         html : '<i class="fa fa-repeat"></i>'
36577                     }
36578                 ]
36579             }
36580         ]
36581     }
36582 });
36583
36584 /*
36585 * Licence: LGPL
36586 */
36587
36588 /**
36589  * @class Roo.bootstrap.DocumentManager
36590  * @extends Roo.bootstrap.Component
36591  * Bootstrap DocumentManager class
36592  * @cfg {String} paramName default 'imageUpload'
36593  * @cfg {String} toolTipName default 'filename'
36594  * @cfg {String} method default POST
36595  * @cfg {String} url action url
36596  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
36597  * @cfg {Boolean} multiple multiple upload default true
36598  * @cfg {Number} thumbSize default 300
36599  * @cfg {String} fieldLabel
36600  * @cfg {Number} labelWidth default 4
36601  * @cfg {String} labelAlign (left|top) default left
36602  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
36603 * @cfg {Number} labellg set the width of label (1-12)
36604  * @cfg {Number} labelmd set the width of label (1-12)
36605  * @cfg {Number} labelsm set the width of label (1-12)
36606  * @cfg {Number} labelxs set the width of label (1-12)
36607  * 
36608  * @constructor
36609  * Create a new DocumentManager
36610  * @param {Object} config The config object
36611  */
36612
36613 Roo.bootstrap.DocumentManager = function(config){
36614     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
36615     
36616     this.files = [];
36617     this.delegates = [];
36618     
36619     this.addEvents({
36620         /**
36621          * @event initial
36622          * Fire when initial the DocumentManager
36623          * @param {Roo.bootstrap.DocumentManager} this
36624          */
36625         "initial" : true,
36626         /**
36627          * @event inspect
36628          * inspect selected file
36629          * @param {Roo.bootstrap.DocumentManager} this
36630          * @param {File} file
36631          */
36632         "inspect" : true,
36633         /**
36634          * @event exception
36635          * Fire when xhr load exception
36636          * @param {Roo.bootstrap.DocumentManager} this
36637          * @param {XMLHttpRequest} xhr
36638          */
36639         "exception" : true,
36640         /**
36641          * @event afterupload
36642          * Fire when xhr load exception
36643          * @param {Roo.bootstrap.DocumentManager} this
36644          * @param {XMLHttpRequest} xhr
36645          */
36646         "afterupload" : true,
36647         /**
36648          * @event prepare
36649          * prepare the form data
36650          * @param {Roo.bootstrap.DocumentManager} this
36651          * @param {Object} formData
36652          */
36653         "prepare" : true,
36654         /**
36655          * @event remove
36656          * Fire when remove the file
36657          * @param {Roo.bootstrap.DocumentManager} this
36658          * @param {Object} file
36659          */
36660         "remove" : true,
36661         /**
36662          * @event refresh
36663          * Fire after refresh the file
36664          * @param {Roo.bootstrap.DocumentManager} this
36665          */
36666         "refresh" : true,
36667         /**
36668          * @event click
36669          * Fire after click the image
36670          * @param {Roo.bootstrap.DocumentManager} this
36671          * @param {Object} file
36672          */
36673         "click" : true,
36674         /**
36675          * @event edit
36676          * Fire when upload a image and editable set to true
36677          * @param {Roo.bootstrap.DocumentManager} this
36678          * @param {Object} file
36679          */
36680         "edit" : true,
36681         /**
36682          * @event beforeselectfile
36683          * Fire before select file
36684          * @param {Roo.bootstrap.DocumentManager} this
36685          */
36686         "beforeselectfile" : true,
36687         /**
36688          * @event process
36689          * Fire before process file
36690          * @param {Roo.bootstrap.DocumentManager} this
36691          * @param {Object} file
36692          */
36693         "process" : true,
36694         /**
36695          * @event previewrendered
36696          * Fire when preview rendered
36697          * @param {Roo.bootstrap.DocumentManager} this
36698          * @param {Object} file
36699          */
36700         "previewrendered" : true,
36701         /**
36702          */
36703         "previewResize" : true
36704         
36705     });
36706 };
36707
36708 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
36709     
36710     boxes : 0,
36711     inputName : '',
36712     thumbSize : 300,
36713     multiple : true,
36714     files : false,
36715     method : 'POST',
36716     url : '',
36717     paramName : 'imageUpload',
36718     toolTipName : 'filename',
36719     fieldLabel : '',
36720     labelWidth : 4,
36721     labelAlign : 'left',
36722     editable : true,
36723     delegates : false,
36724     xhr : false, 
36725     
36726     labellg : 0,
36727     labelmd : 0,
36728     labelsm : 0,
36729     labelxs : 0,
36730     
36731     getAutoCreate : function()
36732     {   
36733         var managerWidget = {
36734             tag : 'div',
36735             cls : 'roo-document-manager',
36736             cn : [
36737                 {
36738                     tag : 'input',
36739                     cls : 'roo-document-manager-selector',
36740                     type : 'file'
36741                 },
36742                 {
36743                     tag : 'div',
36744                     cls : 'roo-document-manager-uploader',
36745                     cn : [
36746                         {
36747                             tag : 'div',
36748                             cls : 'roo-document-manager-upload-btn',
36749                             html : '<i class="fa fa-plus"></i>'
36750                         }
36751                     ]
36752                     
36753                 }
36754             ]
36755         };
36756         
36757         var content = [
36758             {
36759                 tag : 'div',
36760                 cls : 'column col-md-12',
36761                 cn : managerWidget
36762             }
36763         ];
36764         
36765         if(this.fieldLabel.length){
36766             
36767             content = [
36768                 {
36769                     tag : 'div',
36770                     cls : 'column col-md-12',
36771                     html : this.fieldLabel
36772                 },
36773                 {
36774                     tag : 'div',
36775                     cls : 'column col-md-12',
36776                     cn : managerWidget
36777                 }
36778             ];
36779
36780             if(this.labelAlign == 'left'){
36781                 content = [
36782                     {
36783                         tag : 'div',
36784                         cls : 'column',
36785                         html : this.fieldLabel
36786                     },
36787                     {
36788                         tag : 'div',
36789                         cls : 'column',
36790                         cn : managerWidget
36791                     }
36792                 ];
36793                 
36794                 if(this.labelWidth > 12){
36795                     content[0].style = "width: " + this.labelWidth + 'px';
36796                 }
36797
36798                 if(this.labelWidth < 13 && this.labelmd == 0){
36799                     this.labelmd = this.labelWidth;
36800                 }
36801
36802                 if(this.labellg > 0){
36803                     content[0].cls += ' col-lg-' + this.labellg;
36804                     content[1].cls += ' col-lg-' + (12 - this.labellg);
36805                 }
36806
36807                 if(this.labelmd > 0){
36808                     content[0].cls += ' col-md-' + this.labelmd;
36809                     content[1].cls += ' col-md-' + (12 - this.labelmd);
36810                 }
36811
36812                 if(this.labelsm > 0){
36813                     content[0].cls += ' col-sm-' + this.labelsm;
36814                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
36815                 }
36816
36817                 if(this.labelxs > 0){
36818                     content[0].cls += ' col-xs-' + this.labelxs;
36819                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
36820                 }
36821                 
36822             }
36823         }
36824         
36825         var cfg = {
36826             tag : 'div',
36827             cls : 'row clearfix',
36828             cn : content
36829         };
36830         
36831         return cfg;
36832         
36833     },
36834     
36835     initEvents : function()
36836     {
36837         this.managerEl = this.el.select('.roo-document-manager', true).first();
36838         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36839         
36840         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
36841         this.selectorEl.hide();
36842         
36843         if(this.multiple){
36844             this.selectorEl.attr('multiple', 'multiple');
36845         }
36846         
36847         this.selectorEl.on('change', this.onFileSelected, this);
36848         
36849         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
36850         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36851         
36852         this.uploader.on('click', this.onUploaderClick, this);
36853         
36854         this.renderProgressDialog();
36855         
36856         var _this = this;
36857         
36858         window.addEventListener("resize", function() { _this.refresh(); } );
36859         
36860         this.fireEvent('initial', this);
36861     },
36862     
36863     renderProgressDialog : function()
36864     {
36865         var _this = this;
36866         
36867         this.progressDialog = new Roo.bootstrap.Modal({
36868             cls : 'roo-document-manager-progress-dialog',
36869             allow_close : false,
36870             animate : false,
36871             title : '',
36872             buttons : [
36873                 {
36874                     name  :'cancel',
36875                     weight : 'danger',
36876                     html : 'Cancel'
36877                 }
36878             ], 
36879             listeners : { 
36880                 btnclick : function() {
36881                     _this.uploadCancel();
36882                     this.hide();
36883                 }
36884             }
36885         });
36886          
36887         this.progressDialog.render(Roo.get(document.body));
36888          
36889         this.progress = new Roo.bootstrap.Progress({
36890             cls : 'roo-document-manager-progress',
36891             active : true,
36892             striped : true
36893         });
36894         
36895         this.progress.render(this.progressDialog.getChildContainer());
36896         
36897         this.progressBar = new Roo.bootstrap.ProgressBar({
36898             cls : 'roo-document-manager-progress-bar',
36899             aria_valuenow : 0,
36900             aria_valuemin : 0,
36901             aria_valuemax : 12,
36902             panel : 'success'
36903         });
36904         
36905         this.progressBar.render(this.progress.getChildContainer());
36906     },
36907     
36908     onUploaderClick : function(e)
36909     {
36910         e.preventDefault();
36911      
36912         if(this.fireEvent('beforeselectfile', this) != false){
36913             this.selectorEl.dom.click();
36914         }
36915         
36916     },
36917     
36918     onFileSelected : function(e)
36919     {
36920         e.preventDefault();
36921         
36922         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36923             return;
36924         }
36925         
36926         Roo.each(this.selectorEl.dom.files, function(file){
36927             if(this.fireEvent('inspect', this, file) != false){
36928                 this.files.push(file);
36929             }
36930         }, this);
36931         
36932         this.queue();
36933         
36934     },
36935     
36936     queue : function()
36937     {
36938         this.selectorEl.dom.value = '';
36939         
36940         if(!this.files || !this.files.length){
36941             return;
36942         }
36943         
36944         if(this.boxes > 0 && this.files.length > this.boxes){
36945             this.files = this.files.slice(0, this.boxes);
36946         }
36947         
36948         this.uploader.show();
36949         
36950         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36951             this.uploader.hide();
36952         }
36953         
36954         var _this = this;
36955         
36956         var files = [];
36957         
36958         var docs = [];
36959         
36960         Roo.each(this.files, function(file){
36961             
36962             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36963                 var f = this.renderPreview(file);
36964                 files.push(f);
36965                 return;
36966             }
36967             
36968             if(file.type.indexOf('image') != -1){
36969                 this.delegates.push(
36970                     (function(){
36971                         _this.process(file);
36972                     }).createDelegate(this)
36973                 );
36974         
36975                 return;
36976             }
36977             
36978             docs.push(
36979                 (function(){
36980                     _this.process(file);
36981                 }).createDelegate(this)
36982             );
36983             
36984         }, this);
36985         
36986         this.files = files;
36987         
36988         this.delegates = this.delegates.concat(docs);
36989         
36990         if(!this.delegates.length){
36991             this.refresh();
36992             return;
36993         }
36994         
36995         this.progressBar.aria_valuemax = this.delegates.length;
36996         
36997         this.arrange();
36998         
36999         return;
37000     },
37001     
37002     arrange : function()
37003     {
37004         if(!this.delegates.length){
37005             this.progressDialog.hide();
37006             this.refresh();
37007             return;
37008         }
37009         
37010         var delegate = this.delegates.shift();
37011         
37012         this.progressDialog.show();
37013         
37014         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37015         
37016         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37017         
37018         delegate();
37019     },
37020     
37021     refresh : function()
37022     {
37023         this.uploader.show();
37024         
37025         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37026             this.uploader.hide();
37027         }
37028         
37029         Roo.isTouch ? this.closable(false) : this.closable(true);
37030         
37031         this.fireEvent('refresh', this);
37032     },
37033     
37034     onRemove : function(e, el, o)
37035     {
37036         e.preventDefault();
37037         
37038         this.fireEvent('remove', this, o);
37039         
37040     },
37041     
37042     remove : function(o)
37043     {
37044         var files = [];
37045         
37046         Roo.each(this.files, function(file){
37047             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37048                 files.push(file);
37049                 return;
37050             }
37051
37052             o.target.remove();
37053
37054         }, this);
37055         
37056         this.files = files;
37057         
37058         this.refresh();
37059     },
37060     
37061     clear : function()
37062     {
37063         Roo.each(this.files, function(file){
37064             if(!file.target){
37065                 return;
37066             }
37067             
37068             file.target.remove();
37069
37070         }, this);
37071         
37072         this.files = [];
37073         
37074         this.refresh();
37075     },
37076     
37077     onClick : function(e, el, o)
37078     {
37079         e.preventDefault();
37080         
37081         this.fireEvent('click', this, o);
37082         
37083     },
37084     
37085     closable : function(closable)
37086     {
37087         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37088             
37089             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37090             
37091             if(closable){
37092                 el.show();
37093                 return;
37094             }
37095             
37096             el.hide();
37097             
37098         }, this);
37099     },
37100     
37101     xhrOnLoad : function(xhr)
37102     {
37103         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37104             el.remove();
37105         }, this);
37106         
37107         if (xhr.readyState !== 4) {
37108             this.arrange();
37109             this.fireEvent('exception', this, xhr);
37110             return;
37111         }
37112
37113         var response = Roo.decode(xhr.responseText);
37114         
37115         if(!response.success){
37116             this.arrange();
37117             this.fireEvent('exception', this, xhr);
37118             return;
37119         }
37120         
37121         var file = this.renderPreview(response.data);
37122         
37123         this.files.push(file);
37124         
37125         this.arrange();
37126         
37127         this.fireEvent('afterupload', this, xhr);
37128         
37129     },
37130     
37131     xhrOnError : function(xhr)
37132     {
37133         Roo.log('xhr on error');
37134         
37135         var response = Roo.decode(xhr.responseText);
37136           
37137         Roo.log(response);
37138         
37139         this.arrange();
37140     },
37141     
37142     process : function(file)
37143     {
37144         if(this.fireEvent('process', this, file) !== false){
37145             if(this.editable && file.type.indexOf('image') != -1){
37146                 this.fireEvent('edit', this, file);
37147                 return;
37148             }
37149
37150             this.uploadStart(file, false);
37151
37152             return;
37153         }
37154         
37155     },
37156     
37157     uploadStart : function(file, crop)
37158     {
37159         this.xhr = new XMLHttpRequest();
37160         
37161         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37162             this.arrange();
37163             return;
37164         }
37165         
37166         file.xhr = this.xhr;
37167             
37168         this.managerEl.createChild({
37169             tag : 'div',
37170             cls : 'roo-document-manager-loading',
37171             cn : [
37172                 {
37173                     tag : 'div',
37174                     tooltip : file.name,
37175                     cls : 'roo-document-manager-thumb',
37176                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37177                 }
37178             ]
37179
37180         });
37181
37182         this.xhr.open(this.method, this.url, true);
37183         
37184         var headers = {
37185             "Accept": "application/json",
37186             "Cache-Control": "no-cache",
37187             "X-Requested-With": "XMLHttpRequest"
37188         };
37189         
37190         for (var headerName in headers) {
37191             var headerValue = headers[headerName];
37192             if (headerValue) {
37193                 this.xhr.setRequestHeader(headerName, headerValue);
37194             }
37195         }
37196         
37197         var _this = this;
37198         
37199         this.xhr.onload = function()
37200         {
37201             _this.xhrOnLoad(_this.xhr);
37202         }
37203         
37204         this.xhr.onerror = function()
37205         {
37206             _this.xhrOnError(_this.xhr);
37207         }
37208         
37209         var formData = new FormData();
37210
37211         formData.append('returnHTML', 'NO');
37212         
37213         if(crop){
37214             formData.append('crop', crop);
37215         }
37216         
37217         formData.append(this.paramName, file, file.name);
37218         
37219         var options = {
37220             file : file, 
37221             manually : false
37222         };
37223         
37224         if(this.fireEvent('prepare', this, formData, options) != false){
37225             
37226             if(options.manually){
37227                 return;
37228             }
37229             
37230             this.xhr.send(formData);
37231             return;
37232         };
37233         
37234         this.uploadCancel();
37235     },
37236     
37237     uploadCancel : function()
37238     {
37239         if (this.xhr) {
37240             this.xhr.abort();
37241         }
37242         
37243         this.delegates = [];
37244         
37245         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37246             el.remove();
37247         }, this);
37248         
37249         this.arrange();
37250     },
37251     
37252     renderPreview : function(file)
37253     {
37254         if(typeof(file.target) != 'undefined' && file.target){
37255             return file;
37256         }
37257         
37258         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37259         
37260         var previewEl = this.managerEl.createChild({
37261             tag : 'div',
37262             cls : 'roo-document-manager-preview',
37263             cn : [
37264                 {
37265                     tag : 'div',
37266                     tooltip : file[this.toolTipName],
37267                     cls : 'roo-document-manager-thumb',
37268                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37269                 },
37270                 {
37271                     tag : 'button',
37272                     cls : 'close',
37273                     html : '<i class="fa fa-times-circle"></i>'
37274                 }
37275             ]
37276         });
37277
37278         var close = previewEl.select('button.close', true).first();
37279
37280         close.on('click', this.onRemove, this, file);
37281
37282         file.target = previewEl;
37283
37284         var image = previewEl.select('img', true).first();
37285         
37286         var _this = this;
37287         
37288         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37289         
37290         image.on('click', this.onClick, this, file);
37291         
37292         this.fireEvent('previewrendered', this, file);
37293         
37294         return file;
37295         
37296     },
37297     
37298     onPreviewLoad : function(file, image)
37299     {
37300         if(typeof(file.target) == 'undefined' || !file.target){
37301             return;
37302         }
37303         
37304         var width = image.dom.naturalWidth || image.dom.width;
37305         var height = image.dom.naturalHeight || image.dom.height;
37306         
37307         if(!this.previewResize) {
37308             return;
37309         }
37310         
37311         if(width > height){
37312             file.target.addClass('wide');
37313             return;
37314         }
37315         
37316         file.target.addClass('tall');
37317         return;
37318         
37319     },
37320     
37321     uploadFromSource : function(file, crop)
37322     {
37323         this.xhr = new XMLHttpRequest();
37324         
37325         this.managerEl.createChild({
37326             tag : 'div',
37327             cls : 'roo-document-manager-loading',
37328             cn : [
37329                 {
37330                     tag : 'div',
37331                     tooltip : file.name,
37332                     cls : 'roo-document-manager-thumb',
37333                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37334                 }
37335             ]
37336
37337         });
37338
37339         this.xhr.open(this.method, this.url, true);
37340         
37341         var headers = {
37342             "Accept": "application/json",
37343             "Cache-Control": "no-cache",
37344             "X-Requested-With": "XMLHttpRequest"
37345         };
37346         
37347         for (var headerName in headers) {
37348             var headerValue = headers[headerName];
37349             if (headerValue) {
37350                 this.xhr.setRequestHeader(headerName, headerValue);
37351             }
37352         }
37353         
37354         var _this = this;
37355         
37356         this.xhr.onload = function()
37357         {
37358             _this.xhrOnLoad(_this.xhr);
37359         }
37360         
37361         this.xhr.onerror = function()
37362         {
37363             _this.xhrOnError(_this.xhr);
37364         }
37365         
37366         var formData = new FormData();
37367
37368         formData.append('returnHTML', 'NO');
37369         
37370         formData.append('crop', crop);
37371         
37372         if(typeof(file.filename) != 'undefined'){
37373             formData.append('filename', file.filename);
37374         }
37375         
37376         if(typeof(file.mimetype) != 'undefined'){
37377             formData.append('mimetype', file.mimetype);
37378         }
37379         
37380         Roo.log(formData);
37381         
37382         if(this.fireEvent('prepare', this, formData) != false){
37383             this.xhr.send(formData);
37384         };
37385     }
37386 });
37387
37388 /*
37389 * Licence: LGPL
37390 */
37391
37392 /**
37393  * @class Roo.bootstrap.DocumentViewer
37394  * @extends Roo.bootstrap.Component
37395  * Bootstrap DocumentViewer class
37396  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37397  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37398  * 
37399  * @constructor
37400  * Create a new DocumentViewer
37401  * @param {Object} config The config object
37402  */
37403
37404 Roo.bootstrap.DocumentViewer = function(config){
37405     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37406     
37407     this.addEvents({
37408         /**
37409          * @event initial
37410          * Fire after initEvent
37411          * @param {Roo.bootstrap.DocumentViewer} this
37412          */
37413         "initial" : true,
37414         /**
37415          * @event click
37416          * Fire after click
37417          * @param {Roo.bootstrap.DocumentViewer} this
37418          */
37419         "click" : true,
37420         /**
37421          * @event download
37422          * Fire after download button
37423          * @param {Roo.bootstrap.DocumentViewer} this
37424          */
37425         "download" : true,
37426         /**
37427          * @event trash
37428          * Fire after trash button
37429          * @param {Roo.bootstrap.DocumentViewer} this
37430          */
37431         "trash" : true
37432         
37433     });
37434 };
37435
37436 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37437     
37438     showDownload : true,
37439     
37440     showTrash : true,
37441     
37442     getAutoCreate : function()
37443     {
37444         var cfg = {
37445             tag : 'div',
37446             cls : 'roo-document-viewer',
37447             cn : [
37448                 {
37449                     tag : 'div',
37450                     cls : 'roo-document-viewer-body',
37451                     cn : [
37452                         {
37453                             tag : 'div',
37454                             cls : 'roo-document-viewer-thumb',
37455                             cn : [
37456                                 {
37457                                     tag : 'img',
37458                                     cls : 'roo-document-viewer-image'
37459                                 }
37460                             ]
37461                         }
37462                     ]
37463                 },
37464                 {
37465                     tag : 'div',
37466                     cls : 'roo-document-viewer-footer',
37467                     cn : {
37468                         tag : 'div',
37469                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37470                         cn : [
37471                             {
37472                                 tag : 'div',
37473                                 cls : 'btn-group roo-document-viewer-download',
37474                                 cn : [
37475                                     {
37476                                         tag : 'button',
37477                                         cls : 'btn btn-default',
37478                                         html : '<i class="fa fa-download"></i>'
37479                                     }
37480                                 ]
37481                             },
37482                             {
37483                                 tag : 'div',
37484                                 cls : 'btn-group roo-document-viewer-trash',
37485                                 cn : [
37486                                     {
37487                                         tag : 'button',
37488                                         cls : 'btn btn-default',
37489                                         html : '<i class="fa fa-trash"></i>'
37490                                     }
37491                                 ]
37492                             }
37493                         ]
37494                     }
37495                 }
37496             ]
37497         };
37498         
37499         return cfg;
37500     },
37501     
37502     initEvents : function()
37503     {
37504         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37505         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37506         
37507         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37508         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37509         
37510         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37511         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37512         
37513         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37514         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37515         
37516         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37517         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37518         
37519         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37520         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37521         
37522         this.bodyEl.on('click', this.onClick, this);
37523         this.downloadBtn.on('click', this.onDownload, this);
37524         this.trashBtn.on('click', this.onTrash, this);
37525         
37526         this.downloadBtn.hide();
37527         this.trashBtn.hide();
37528         
37529         if(this.showDownload){
37530             this.downloadBtn.show();
37531         }
37532         
37533         if(this.showTrash){
37534             this.trashBtn.show();
37535         }
37536         
37537         if(!this.showDownload && !this.showTrash) {
37538             this.footerEl.hide();
37539         }
37540         
37541     },
37542     
37543     initial : function()
37544     {
37545         this.fireEvent('initial', this);
37546         
37547     },
37548     
37549     onClick : function(e)
37550     {
37551         e.preventDefault();
37552         
37553         this.fireEvent('click', this);
37554     },
37555     
37556     onDownload : function(e)
37557     {
37558         e.preventDefault();
37559         
37560         this.fireEvent('download', this);
37561     },
37562     
37563     onTrash : function(e)
37564     {
37565         e.preventDefault();
37566         
37567         this.fireEvent('trash', this);
37568     }
37569     
37570 });
37571 /*
37572  * - LGPL
37573  *
37574  * FieldLabel
37575  * 
37576  */
37577
37578 /**
37579  * @class Roo.bootstrap.form.FieldLabel
37580  * @extends Roo.bootstrap.Component
37581  * Bootstrap FieldLabel class
37582  * @cfg {String} html contents of the element
37583  * @cfg {String} tag tag of the element default label
37584  * @cfg {String} cls class of the element
37585  * @cfg {String} target label target 
37586  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
37587  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
37588  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
37589  * @cfg {String} iconTooltip default "This field is required"
37590  * @cfg {String} indicatorpos (left|right) default left
37591  * 
37592  * @constructor
37593  * Create a new FieldLabel
37594  * @param {Object} config The config object
37595  */
37596
37597 Roo.bootstrap.form.FieldLabel = function(config){
37598     Roo.bootstrap.Element.superclass.constructor.call(this, config);
37599     
37600     this.addEvents({
37601             /**
37602              * @event invalid
37603              * Fires after the field has been marked as invalid.
37604              * @param {Roo.form.FieldLabel} this
37605              * @param {String} msg The validation message
37606              */
37607             invalid : true,
37608             /**
37609              * @event valid
37610              * Fires after the field has been validated with no errors.
37611              * @param {Roo.form.FieldLabel} this
37612              */
37613             valid : true
37614         });
37615 };
37616
37617 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
37618     
37619     tag: 'label',
37620     cls: '',
37621     html: '',
37622     target: '',
37623     allowBlank : true,
37624     invalidClass : 'has-warning',
37625     validClass : 'has-success',
37626     iconTooltip : 'This field is required',
37627     indicatorpos : 'left',
37628     
37629     getAutoCreate : function(){
37630         
37631         var cls = "";
37632         if (!this.allowBlank) {
37633             cls  = "visible";
37634         }
37635         
37636         var cfg = {
37637             tag : this.tag,
37638             cls : 'roo-bootstrap-field-label ' + this.cls,
37639             for : this.target,
37640             cn : [
37641                 {
37642                     tag : 'i',
37643                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
37644                     tooltip : this.iconTooltip
37645                 },
37646                 {
37647                     tag : 'span',
37648                     html : this.html
37649                 }
37650             ] 
37651         };
37652         
37653         if(this.indicatorpos == 'right'){
37654             var cfg = {
37655                 tag : this.tag,
37656                 cls : 'roo-bootstrap-field-label ' + this.cls,
37657                 for : this.target,
37658                 cn : [
37659                     {
37660                         tag : 'span',
37661                         html : this.html
37662                     },
37663                     {
37664                         tag : 'i',
37665                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
37666                         tooltip : this.iconTooltip
37667                     }
37668                 ] 
37669             };
37670         }
37671         
37672         return cfg;
37673     },
37674     
37675     initEvents: function() 
37676     {
37677         Roo.bootstrap.Element.superclass.initEvents.call(this);
37678         
37679         this.indicator = this.indicatorEl();
37680         
37681         if(this.indicator){
37682             this.indicator.removeClass('visible');
37683             this.indicator.addClass('invisible');
37684         }
37685         
37686         Roo.bootstrap.form.FieldLabel.register(this);
37687     },
37688     
37689     indicatorEl : function()
37690     {
37691         var indicator = this.el.select('i.roo-required-indicator',true).first();
37692         
37693         if(!indicator){
37694             return false;
37695         }
37696         
37697         return indicator;
37698         
37699     },
37700     
37701     /**
37702      * Mark this field as valid
37703      */
37704     markValid : function()
37705     {
37706         if(this.indicator){
37707             this.indicator.removeClass('visible');
37708             this.indicator.addClass('invisible');
37709         }
37710         if (Roo.bootstrap.version == 3) {
37711             this.el.removeClass(this.invalidClass);
37712             this.el.addClass(this.validClass);
37713         } else {
37714             this.el.removeClass('is-invalid');
37715             this.el.addClass('is-valid');
37716         }
37717         
37718         
37719         this.fireEvent('valid', this);
37720     },
37721     
37722     /**
37723      * Mark this field as invalid
37724      * @param {String} msg The validation message
37725      */
37726     markInvalid : function(msg)
37727     {
37728         if(this.indicator){
37729             this.indicator.removeClass('invisible');
37730             this.indicator.addClass('visible');
37731         }
37732           if (Roo.bootstrap.version == 3) {
37733             this.el.removeClass(this.validClass);
37734             this.el.addClass(this.invalidClass);
37735         } else {
37736             this.el.removeClass('is-valid');
37737             this.el.addClass('is-invalid');
37738         }
37739         
37740         
37741         this.fireEvent('invalid', this, msg);
37742     }
37743     
37744    
37745 });
37746
37747 Roo.apply(Roo.bootstrap.form.FieldLabel, {
37748     
37749     groups: {},
37750     
37751      /**
37752     * register a FieldLabel Group
37753     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
37754     */
37755     register : function(label)
37756     {
37757         if(this.groups.hasOwnProperty(label.target)){
37758             return;
37759         }
37760      
37761         this.groups[label.target] = label;
37762         
37763     },
37764     /**
37765     * fetch a FieldLabel Group based on the target
37766     * @param {string} target
37767     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
37768     */
37769     get: function(target) {
37770         if (typeof(this.groups[target]) == 'undefined') {
37771             return false;
37772         }
37773         
37774         return this.groups[target] ;
37775     }
37776 });
37777
37778  
37779
37780  /*
37781  * - LGPL
37782  *
37783  * page DateSplitField.
37784  * 
37785  */
37786
37787
37788 /**
37789  * @class Roo.bootstrap.form.DateSplitField
37790  * @extends Roo.bootstrap.Component
37791  * Bootstrap DateSplitField class
37792  * @cfg {string} fieldLabel - the label associated
37793  * @cfg {Number} labelWidth set the width of label (0-12)
37794  * @cfg {String} labelAlign (top|left)
37795  * @cfg {Boolean} dayAllowBlank (true|false) default false
37796  * @cfg {Boolean} monthAllowBlank (true|false) default false
37797  * @cfg {Boolean} yearAllowBlank (true|false) default false
37798  * @cfg {string} dayPlaceholder 
37799  * @cfg {string} monthPlaceholder
37800  * @cfg {string} yearPlaceholder
37801  * @cfg {string} dayFormat default 'd'
37802  * @cfg {string} monthFormat default 'm'
37803  * @cfg {string} yearFormat default 'Y'
37804  * @cfg {Number} labellg set the width of label (1-12)
37805  * @cfg {Number} labelmd set the width of label (1-12)
37806  * @cfg {Number} labelsm set the width of label (1-12)
37807  * @cfg {Number} labelxs set the width of label (1-12)
37808
37809  *     
37810  * @constructor
37811  * Create a new DateSplitField
37812  * @param {Object} config The config object
37813  */
37814
37815 Roo.bootstrap.form.DateSplitField = function(config){
37816     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
37817     
37818     this.addEvents({
37819         // raw events
37820          /**
37821          * @event years
37822          * getting the data of years
37823          * @param {Roo.bootstrap.form.DateSplitField} this
37824          * @param {Object} years
37825          */
37826         "years" : true,
37827         /**
37828          * @event days
37829          * getting the data of days
37830          * @param {Roo.bootstrap.form.DateSplitField} this
37831          * @param {Object} days
37832          */
37833         "days" : true,
37834         /**
37835          * @event invalid
37836          * Fires after the field has been marked as invalid.
37837          * @param {Roo.form.Field} this
37838          * @param {String} msg The validation message
37839          */
37840         invalid : true,
37841        /**
37842          * @event valid
37843          * Fires after the field has been validated with no errors.
37844          * @param {Roo.form.Field} this
37845          */
37846         valid : true
37847     });
37848 };
37849
37850 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
37851     
37852     fieldLabel : '',
37853     labelAlign : 'top',
37854     labelWidth : 3,
37855     dayAllowBlank : false,
37856     monthAllowBlank : false,
37857     yearAllowBlank : false,
37858     dayPlaceholder : '',
37859     monthPlaceholder : '',
37860     yearPlaceholder : '',
37861     dayFormat : 'd',
37862     monthFormat : 'm',
37863     yearFormat : 'Y',
37864     isFormField : true,
37865     labellg : 0,
37866     labelmd : 0,
37867     labelsm : 0,
37868     labelxs : 0,
37869     
37870     getAutoCreate : function()
37871     {
37872         var cfg = {
37873             tag : 'div',
37874             cls : 'row roo-date-split-field-group',
37875             cn : [
37876                 {
37877                     tag : 'input',
37878                     type : 'hidden',
37879                     cls : 'form-hidden-field roo-date-split-field-group-value',
37880                     name : this.name
37881                 }
37882             ]
37883         };
37884         
37885         var labelCls = 'col-md-12';
37886         var contentCls = 'col-md-4';
37887         
37888         if(this.fieldLabel){
37889             
37890             var label = {
37891                 tag : 'div',
37892                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
37893                 cn : [
37894                     {
37895                         tag : 'label',
37896                         html : this.fieldLabel
37897                     }
37898                 ]
37899             };
37900             
37901             if(this.labelAlign == 'left'){
37902             
37903                 if(this.labelWidth > 12){
37904                     label.style = "width: " + this.labelWidth + 'px';
37905                 }
37906
37907                 if(this.labelWidth < 13 && this.labelmd == 0){
37908                     this.labelmd = this.labelWidth;
37909                 }
37910
37911                 if(this.labellg > 0){
37912                     labelCls = ' col-lg-' + this.labellg;
37913                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
37914                 }
37915
37916                 if(this.labelmd > 0){
37917                     labelCls = ' col-md-' + this.labelmd;
37918                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
37919                 }
37920
37921                 if(this.labelsm > 0){
37922                     labelCls = ' col-sm-' + this.labelsm;
37923                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
37924                 }
37925
37926                 if(this.labelxs > 0){
37927                     labelCls = ' col-xs-' + this.labelxs;
37928                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
37929                 }
37930             }
37931             
37932             label.cls += ' ' + labelCls;
37933             
37934             cfg.cn.push(label);
37935         }
37936         
37937         Roo.each(['day', 'month', 'year'], function(t){
37938             cfg.cn.push({
37939                 tag : 'div',
37940                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
37941             });
37942         }, this);
37943         
37944         return cfg;
37945     },
37946     
37947     inputEl: function ()
37948     {
37949         return this.el.select('.roo-date-split-field-group-value', true).first();
37950     },
37951     
37952     onRender : function(ct, position) 
37953     {
37954         var _this = this;
37955         
37956         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
37957         
37958         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
37959         
37960         this.dayField = new Roo.bootstrap.form.ComboBox({
37961             allowBlank : this.dayAllowBlank,
37962             alwaysQuery : true,
37963             displayField : 'value',
37964             editable : false,
37965             fieldLabel : '',
37966             forceSelection : true,
37967             mode : 'local',
37968             placeholder : this.dayPlaceholder,
37969             selectOnFocus : true,
37970             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37971             triggerAction : 'all',
37972             typeAhead : true,
37973             valueField : 'value',
37974             store : new Roo.data.SimpleStore({
37975                 data : (function() {    
37976                     var days = [];
37977                     _this.fireEvent('days', _this, days);
37978                     return days;
37979                 })(),
37980                 fields : [ 'value' ]
37981             }),
37982             listeners : {
37983                 select : function (_self, record, index)
37984                 {
37985                     _this.setValue(_this.getValue());
37986                 }
37987             }
37988         });
37989
37990         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
37991         
37992         this.monthField = new Roo.bootstrap.form.MonthField({
37993             after : '<i class=\"fa fa-calendar\"></i>',
37994             allowBlank : this.monthAllowBlank,
37995             placeholder : this.monthPlaceholder,
37996             readOnly : true,
37997             listeners : {
37998                 render : function (_self)
37999                 {
38000                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38001                         e.preventDefault();
38002                         _self.focus();
38003                     });
38004                 },
38005                 select : function (_self, oldvalue, newvalue)
38006                 {
38007                     _this.setValue(_this.getValue());
38008                 }
38009             }
38010         });
38011         
38012         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38013         
38014         this.yearField = new Roo.bootstrap.form.ComboBox({
38015             allowBlank : this.yearAllowBlank,
38016             alwaysQuery : true,
38017             displayField : 'value',
38018             editable : false,
38019             fieldLabel : '',
38020             forceSelection : true,
38021             mode : 'local',
38022             placeholder : this.yearPlaceholder,
38023             selectOnFocus : true,
38024             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38025             triggerAction : 'all',
38026             typeAhead : true,
38027             valueField : 'value',
38028             store : new Roo.data.SimpleStore({
38029                 data : (function() {
38030                     var years = [];
38031                     _this.fireEvent('years', _this, years);
38032                     return years;
38033                 })(),
38034                 fields : [ 'value' ]
38035             }),
38036             listeners : {
38037                 select : function (_self, record, index)
38038                 {
38039                     _this.setValue(_this.getValue());
38040                 }
38041             }
38042         });
38043
38044         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38045     },
38046     
38047     setValue : function(v, format)
38048     {
38049         this.inputEl.dom.value = v;
38050         
38051         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38052         
38053         var d = Date.parseDate(v, f);
38054         
38055         if(!d){
38056             this.validate();
38057             return;
38058         }
38059         
38060         this.setDay(d.format(this.dayFormat));
38061         this.setMonth(d.format(this.monthFormat));
38062         this.setYear(d.format(this.yearFormat));
38063         
38064         this.validate();
38065         
38066         return;
38067     },
38068     
38069     setDay : function(v)
38070     {
38071         this.dayField.setValue(v);
38072         this.inputEl.dom.value = this.getValue();
38073         this.validate();
38074         return;
38075     },
38076     
38077     setMonth : function(v)
38078     {
38079         this.monthField.setValue(v, true);
38080         this.inputEl.dom.value = this.getValue();
38081         this.validate();
38082         return;
38083     },
38084     
38085     setYear : function(v)
38086     {
38087         this.yearField.setValue(v);
38088         this.inputEl.dom.value = this.getValue();
38089         this.validate();
38090         return;
38091     },
38092     
38093     getDay : function()
38094     {
38095         return this.dayField.getValue();
38096     },
38097     
38098     getMonth : function()
38099     {
38100         return this.monthField.getValue();
38101     },
38102     
38103     getYear : function()
38104     {
38105         return this.yearField.getValue();
38106     },
38107     
38108     getValue : function()
38109     {
38110         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38111         
38112         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38113         
38114         return date;
38115     },
38116     
38117     reset : function()
38118     {
38119         this.setDay('');
38120         this.setMonth('');
38121         this.setYear('');
38122         this.inputEl.dom.value = '';
38123         this.validate();
38124         return;
38125     },
38126     
38127     validate : function()
38128     {
38129         var d = this.dayField.validate();
38130         var m = this.monthField.validate();
38131         var y = this.yearField.validate();
38132         
38133         var valid = true;
38134         
38135         if(
38136                 (!this.dayAllowBlank && !d) ||
38137                 (!this.monthAllowBlank && !m) ||
38138                 (!this.yearAllowBlank && !y)
38139         ){
38140             valid = false;
38141         }
38142         
38143         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38144             return valid;
38145         }
38146         
38147         if(valid){
38148             this.markValid();
38149             return valid;
38150         }
38151         
38152         this.markInvalid();
38153         
38154         return valid;
38155     },
38156     
38157     markValid : function()
38158     {
38159         
38160         var label = this.el.select('label', true).first();
38161         var icon = this.el.select('i.fa-star', true).first();
38162
38163         if(label && icon){
38164             icon.remove();
38165         }
38166         
38167         this.fireEvent('valid', this);
38168     },
38169     
38170      /**
38171      * Mark this field as invalid
38172      * @param {String} msg The validation message
38173      */
38174     markInvalid : function(msg)
38175     {
38176         
38177         var label = this.el.select('label', true).first();
38178         var icon = this.el.select('i.fa-star', true).first();
38179
38180         if(label && !icon){
38181             this.el.select('.roo-date-split-field-label', true).createChild({
38182                 tag : 'i',
38183                 cls : 'text-danger fa fa-lg fa-star',
38184                 tooltip : 'This field is required',
38185                 style : 'margin-right:5px;'
38186             }, label, true);
38187         }
38188         
38189         this.fireEvent('invalid', this, msg);
38190     },
38191     
38192     clearInvalid : function()
38193     {
38194         var label = this.el.select('label', true).first();
38195         var icon = this.el.select('i.fa-star', true).first();
38196
38197         if(label && icon){
38198             icon.remove();
38199         }
38200         
38201         this.fireEvent('valid', this);
38202     },
38203     
38204     getName: function()
38205     {
38206         return this.name;
38207     }
38208     
38209 });
38210
38211  
38212
38213 /**
38214  * @class Roo.bootstrap.LayoutMasonry
38215  * @extends Roo.bootstrap.Component
38216  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38217  * Bootstrap Layout Masonry class
38218  *
38219  * This is based on 
38220  * http://masonry.desandro.com
38221  *
38222  * The idea is to render all the bricks based on vertical width...
38223  *
38224  * The original code extends 'outlayer' - we might need to use that....
38225
38226  * @constructor
38227  * Create a new Element
38228  * @param {Object} config The config object
38229  */
38230
38231 Roo.bootstrap.LayoutMasonry = function(config){
38232     
38233     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38234     
38235     this.bricks = [];
38236     
38237     Roo.bootstrap.LayoutMasonry.register(this);
38238     
38239     this.addEvents({
38240         // raw events
38241         /**
38242          * @event layout
38243          * Fire after layout the items
38244          * @param {Roo.bootstrap.LayoutMasonry} this
38245          * @param {Roo.EventObject} e
38246          */
38247         "layout" : true
38248     });
38249     
38250 };
38251
38252 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38253     
38254     /**
38255      * @cfg {Boolean} isLayoutInstant = no animation?
38256      */   
38257     isLayoutInstant : false, // needed?
38258    
38259     /**
38260      * @cfg {Number} boxWidth  width of the columns
38261      */   
38262     boxWidth : 450,
38263     
38264       /**
38265      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38266      */   
38267     boxHeight : 0,
38268     
38269     /**
38270      * @cfg {Number} padWidth padding below box..
38271      */   
38272     padWidth : 10, 
38273     
38274     /**
38275      * @cfg {Number} gutter gutter width..
38276      */   
38277     gutter : 10,
38278     
38279      /**
38280      * @cfg {Number} maxCols maximum number of columns
38281      */   
38282     
38283     maxCols: 0,
38284     
38285     /**
38286      * @cfg {Boolean} isAutoInitial defalut true
38287      */   
38288     isAutoInitial : true, 
38289     
38290     containerWidth: 0,
38291     
38292     /**
38293      * @cfg {Boolean} isHorizontal defalut false
38294      */   
38295     isHorizontal : false, 
38296
38297     currentSize : null,
38298     
38299     tag: 'div',
38300     
38301     cls: '',
38302     
38303     bricks: null, //CompositeElement
38304     
38305     cols : 1,
38306     
38307     _isLayoutInited : false,
38308     
38309 //    isAlternative : false, // only use for vertical layout...
38310     
38311     /**
38312      * @cfg {Number} alternativePadWidth padding below box..
38313      */   
38314     alternativePadWidth : 50,
38315     
38316     selectedBrick : [],
38317     
38318     getAutoCreate : function(){
38319         
38320         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38321         
38322         var cfg = {
38323             tag: this.tag,
38324             cls: 'blog-masonary-wrapper ' + this.cls,
38325             cn : {
38326                 cls : 'mas-boxes masonary'
38327             }
38328         };
38329         
38330         return cfg;
38331     },
38332     
38333     getChildContainer: function( )
38334     {
38335         if (this.boxesEl) {
38336             return this.boxesEl;
38337         }
38338         
38339         this.boxesEl = this.el.select('.mas-boxes').first();
38340         
38341         return this.boxesEl;
38342     },
38343     
38344     
38345     initEvents : function()
38346     {
38347         var _this = this;
38348         
38349         if(this.isAutoInitial){
38350             Roo.log('hook children rendered');
38351             this.on('childrenrendered', function() {
38352                 Roo.log('children rendered');
38353                 _this.initial();
38354             } ,this);
38355         }
38356     },
38357     
38358     initial : function()
38359     {
38360         this.selectedBrick = [];
38361         
38362         this.currentSize = this.el.getBox(true);
38363         
38364         Roo.EventManager.onWindowResize(this.resize, this); 
38365
38366         if(!this.isAutoInitial){
38367             this.layout();
38368             return;
38369         }
38370         
38371         this.layout();
38372         
38373         return;
38374         //this.layout.defer(500,this);
38375         
38376     },
38377     
38378     resize : function()
38379     {
38380         var cs = this.el.getBox(true);
38381         
38382         if (
38383                 this.currentSize.width == cs.width && 
38384                 this.currentSize.x == cs.x && 
38385                 this.currentSize.height == cs.height && 
38386                 this.currentSize.y == cs.y 
38387         ) {
38388             Roo.log("no change in with or X or Y");
38389             return;
38390         }
38391         
38392         this.currentSize = cs;
38393         
38394         this.layout();
38395         
38396     },
38397     
38398     layout : function()
38399     {   
38400         this._resetLayout();
38401         
38402         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38403         
38404         this.layoutItems( isInstant );
38405       
38406         this._isLayoutInited = true;
38407         
38408         this.fireEvent('layout', this);
38409         
38410     },
38411     
38412     _resetLayout : function()
38413     {
38414         if(this.isHorizontal){
38415             this.horizontalMeasureColumns();
38416             return;
38417         }
38418         
38419         this.verticalMeasureColumns();
38420         
38421     },
38422     
38423     verticalMeasureColumns : function()
38424     {
38425         this.getContainerWidth();
38426         
38427 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38428 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38429 //            return;
38430 //        }
38431         
38432         var boxWidth = this.boxWidth + this.padWidth;
38433         
38434         if(this.containerWidth < this.boxWidth){
38435             boxWidth = this.containerWidth
38436         }
38437         
38438         var containerWidth = this.containerWidth;
38439         
38440         var cols = Math.floor(containerWidth / boxWidth);
38441         
38442         this.cols = Math.max( cols, 1 );
38443         
38444         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38445         
38446         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38447         
38448         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38449         
38450         this.colWidth = boxWidth + avail - this.padWidth;
38451         
38452         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38453         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38454     },
38455     
38456     horizontalMeasureColumns : function()
38457     {
38458         this.getContainerWidth();
38459         
38460         var boxWidth = this.boxWidth;
38461         
38462         if(this.containerWidth < boxWidth){
38463             boxWidth = this.containerWidth;
38464         }
38465         
38466         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38467         
38468         this.el.setHeight(boxWidth);
38469         
38470     },
38471     
38472     getContainerWidth : function()
38473     {
38474         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38475     },
38476     
38477     layoutItems : function( isInstant )
38478     {
38479         Roo.log(this.bricks);
38480         
38481         var items = Roo.apply([], this.bricks);
38482         
38483         if(this.isHorizontal){
38484             this._horizontalLayoutItems( items , isInstant );
38485             return;
38486         }
38487         
38488 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38489 //            this._verticalAlternativeLayoutItems( items , isInstant );
38490 //            return;
38491 //        }
38492         
38493         this._verticalLayoutItems( items , isInstant );
38494         
38495     },
38496     
38497     _verticalLayoutItems : function ( items , isInstant)
38498     {
38499         if ( !items || !items.length ) {
38500             return;
38501         }
38502         
38503         var standard = [
38504             ['xs', 'xs', 'xs', 'tall'],
38505             ['xs', 'xs', 'tall'],
38506             ['xs', 'xs', 'sm'],
38507             ['xs', 'xs', 'xs'],
38508             ['xs', 'tall'],
38509             ['xs', 'sm'],
38510             ['xs', 'xs'],
38511             ['xs'],
38512             
38513             ['sm', 'xs', 'xs'],
38514             ['sm', 'xs'],
38515             ['sm'],
38516             
38517             ['tall', 'xs', 'xs', 'xs'],
38518             ['tall', 'xs', 'xs'],
38519             ['tall', 'xs'],
38520             ['tall']
38521             
38522         ];
38523         
38524         var queue = [];
38525         
38526         var boxes = [];
38527         
38528         var box = [];
38529         
38530         Roo.each(items, function(item, k){
38531             
38532             switch (item.size) {
38533                 // these layouts take up a full box,
38534                 case 'md' :
38535                 case 'md-left' :
38536                 case 'md-right' :
38537                 case 'wide' :
38538                     
38539                     if(box.length){
38540                         boxes.push(box);
38541                         box = [];
38542                     }
38543                     
38544                     boxes.push([item]);
38545                     
38546                     break;
38547                     
38548                 case 'xs' :
38549                 case 'sm' :
38550                 case 'tall' :
38551                     
38552                     box.push(item);
38553                     
38554                     break;
38555                 default :
38556                     break;
38557                     
38558             }
38559             
38560         }, this);
38561         
38562         if(box.length){
38563             boxes.push(box);
38564             box = [];
38565         }
38566         
38567         var filterPattern = function(box, length)
38568         {
38569             if(!box.length){
38570                 return;
38571             }
38572             
38573             var match = false;
38574             
38575             var pattern = box.slice(0, length);
38576             
38577             var format = [];
38578             
38579             Roo.each(pattern, function(i){
38580                 format.push(i.size);
38581             }, this);
38582             
38583             Roo.each(standard, function(s){
38584                 
38585                 if(String(s) != String(format)){
38586                     return;
38587                 }
38588                 
38589                 match = true;
38590                 return false;
38591                 
38592             }, this);
38593             
38594             if(!match && length == 1){
38595                 return;
38596             }
38597             
38598             if(!match){
38599                 filterPattern(box, length - 1);
38600                 return;
38601             }
38602                 
38603             queue.push(pattern);
38604
38605             box = box.slice(length, box.length);
38606
38607             filterPattern(box, 4);
38608
38609             return;
38610             
38611         }
38612         
38613         Roo.each(boxes, function(box, k){
38614             
38615             if(!box.length){
38616                 return;
38617             }
38618             
38619             if(box.length == 1){
38620                 queue.push(box);
38621                 return;
38622             }
38623             
38624             filterPattern(box, 4);
38625             
38626         }, this);
38627         
38628         this._processVerticalLayoutQueue( queue, isInstant );
38629         
38630     },
38631     
38632 //    _verticalAlternativeLayoutItems : function( items , isInstant )
38633 //    {
38634 //        if ( !items || !items.length ) {
38635 //            return;
38636 //        }
38637 //
38638 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
38639 //        
38640 //    },
38641     
38642     _horizontalLayoutItems : function ( items , isInstant)
38643     {
38644         if ( !items || !items.length || items.length < 3) {
38645             return;
38646         }
38647         
38648         items.reverse();
38649         
38650         var eItems = items.slice(0, 3);
38651         
38652         items = items.slice(3, items.length);
38653         
38654         var standard = [
38655             ['xs', 'xs', 'xs', 'wide'],
38656             ['xs', 'xs', 'wide'],
38657             ['xs', 'xs', 'sm'],
38658             ['xs', 'xs', 'xs'],
38659             ['xs', 'wide'],
38660             ['xs', 'sm'],
38661             ['xs', 'xs'],
38662             ['xs'],
38663             
38664             ['sm', 'xs', 'xs'],
38665             ['sm', 'xs'],
38666             ['sm'],
38667             
38668             ['wide', 'xs', 'xs', 'xs'],
38669             ['wide', 'xs', 'xs'],
38670             ['wide', 'xs'],
38671             ['wide'],
38672             
38673             ['wide-thin']
38674         ];
38675         
38676         var queue = [];
38677         
38678         var boxes = [];
38679         
38680         var box = [];
38681         
38682         Roo.each(items, function(item, k){
38683             
38684             switch (item.size) {
38685                 case 'md' :
38686                 case 'md-left' :
38687                 case 'md-right' :
38688                 case 'tall' :
38689                     
38690                     if(box.length){
38691                         boxes.push(box);
38692                         box = [];
38693                     }
38694                     
38695                     boxes.push([item]);
38696                     
38697                     break;
38698                     
38699                 case 'xs' :
38700                 case 'sm' :
38701                 case 'wide' :
38702                 case 'wide-thin' :
38703                     
38704                     box.push(item);
38705                     
38706                     break;
38707                 default :
38708                     break;
38709                     
38710             }
38711             
38712         }, this);
38713         
38714         if(box.length){
38715             boxes.push(box);
38716             box = [];
38717         }
38718         
38719         var filterPattern = function(box, length)
38720         {
38721             if(!box.length){
38722                 return;
38723             }
38724             
38725             var match = false;
38726             
38727             var pattern = box.slice(0, length);
38728             
38729             var format = [];
38730             
38731             Roo.each(pattern, function(i){
38732                 format.push(i.size);
38733             }, this);
38734             
38735             Roo.each(standard, function(s){
38736                 
38737                 if(String(s) != String(format)){
38738                     return;
38739                 }
38740                 
38741                 match = true;
38742                 return false;
38743                 
38744             }, this);
38745             
38746             if(!match && length == 1){
38747                 return;
38748             }
38749             
38750             if(!match){
38751                 filterPattern(box, length - 1);
38752                 return;
38753             }
38754                 
38755             queue.push(pattern);
38756
38757             box = box.slice(length, box.length);
38758
38759             filterPattern(box, 4);
38760
38761             return;
38762             
38763         }
38764         
38765         Roo.each(boxes, function(box, k){
38766             
38767             if(!box.length){
38768                 return;
38769             }
38770             
38771             if(box.length == 1){
38772                 queue.push(box);
38773                 return;
38774             }
38775             
38776             filterPattern(box, 4);
38777             
38778         }, this);
38779         
38780         
38781         var prune = [];
38782         
38783         var pos = this.el.getBox(true);
38784         
38785         var minX = pos.x;
38786         
38787         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38788         
38789         var hit_end = false;
38790         
38791         Roo.each(queue, function(box){
38792             
38793             if(hit_end){
38794                 
38795                 Roo.each(box, function(b){
38796                 
38797                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38798                     b.el.hide();
38799
38800                 }, this);
38801
38802                 return;
38803             }
38804             
38805             var mx = 0;
38806             
38807             Roo.each(box, function(b){
38808                 
38809                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38810                 b.el.show();
38811
38812                 mx = Math.max(mx, b.x);
38813                 
38814             }, this);
38815             
38816             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
38817             
38818             if(maxX < minX){
38819                 
38820                 Roo.each(box, function(b){
38821                 
38822                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38823                     b.el.hide();
38824                     
38825                 }, this);
38826                 
38827                 hit_end = true;
38828                 
38829                 return;
38830             }
38831             
38832             prune.push(box);
38833             
38834         }, this);
38835         
38836         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
38837     },
38838     
38839     /** Sets position of item in DOM
38840     * @param {Element} item
38841     * @param {Number} x - horizontal position
38842     * @param {Number} y - vertical position
38843     * @param {Boolean} isInstant - disables transitions
38844     */
38845     _processVerticalLayoutQueue : function( queue, isInstant )
38846     {
38847         var pos = this.el.getBox(true);
38848         var x = pos.x;
38849         var y = pos.y;
38850         var maxY = [];
38851         
38852         for (var i = 0; i < this.cols; i++){
38853             maxY[i] = pos.y;
38854         }
38855         
38856         Roo.each(queue, function(box, k){
38857             
38858             var col = k % this.cols;
38859             
38860             Roo.each(box, function(b,kk){
38861                 
38862                 b.el.position('absolute');
38863                 
38864                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38865                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38866                 
38867                 if(b.size == 'md-left' || b.size == 'md-right'){
38868                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38869                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38870                 }
38871                 
38872                 b.el.setWidth(width);
38873                 b.el.setHeight(height);
38874                 // iframe?
38875                 b.el.select('iframe',true).setSize(width,height);
38876                 
38877             }, this);
38878             
38879             for (var i = 0; i < this.cols; i++){
38880                 
38881                 if(maxY[i] < maxY[col]){
38882                     col = i;
38883                     continue;
38884                 }
38885                 
38886                 col = Math.min(col, i);
38887                 
38888             }
38889             
38890             x = pos.x + col * (this.colWidth + this.padWidth);
38891             
38892             y = maxY[col];
38893             
38894             var positions = [];
38895             
38896             switch (box.length){
38897                 case 1 :
38898                     positions = this.getVerticalOneBoxColPositions(x, y, box);
38899                     break;
38900                 case 2 :
38901                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
38902                     break;
38903                 case 3 :
38904                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
38905                     break;
38906                 case 4 :
38907                     positions = this.getVerticalFourBoxColPositions(x, y, box);
38908                     break;
38909                 default :
38910                     break;
38911             }
38912             
38913             Roo.each(box, function(b,kk){
38914                 
38915                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38916                 
38917                 var sz = b.el.getSize();
38918                 
38919                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
38920                 
38921             }, this);
38922             
38923         }, this);
38924         
38925         var mY = 0;
38926         
38927         for (var i = 0; i < this.cols; i++){
38928             mY = Math.max(mY, maxY[i]);
38929         }
38930         
38931         this.el.setHeight(mY - pos.y);
38932         
38933     },
38934     
38935 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
38936 //    {
38937 //        var pos = this.el.getBox(true);
38938 //        var x = pos.x;
38939 //        var y = pos.y;
38940 //        var maxX = pos.right;
38941 //        
38942 //        var maxHeight = 0;
38943 //        
38944 //        Roo.each(items, function(item, k){
38945 //            
38946 //            var c = k % 2;
38947 //            
38948 //            item.el.position('absolute');
38949 //                
38950 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
38951 //
38952 //            item.el.setWidth(width);
38953 //
38954 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
38955 //
38956 //            item.el.setHeight(height);
38957 //            
38958 //            if(c == 0){
38959 //                item.el.setXY([x, y], isInstant ? false : true);
38960 //            } else {
38961 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
38962 //            }
38963 //            
38964 //            y = y + height + this.alternativePadWidth;
38965 //            
38966 //            maxHeight = maxHeight + height + this.alternativePadWidth;
38967 //            
38968 //        }, this);
38969 //        
38970 //        this.el.setHeight(maxHeight);
38971 //        
38972 //    },
38973     
38974     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
38975     {
38976         var pos = this.el.getBox(true);
38977         
38978         var minX = pos.x;
38979         var minY = pos.y;
38980         
38981         var maxX = pos.right;
38982         
38983         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
38984         
38985         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38986         
38987         Roo.each(queue, function(box, k){
38988             
38989             Roo.each(box, function(b, kk){
38990                 
38991                 b.el.position('absolute');
38992                 
38993                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38994                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38995                 
38996                 if(b.size == 'md-left' || b.size == 'md-right'){
38997                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38998                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38999                 }
39000                 
39001                 b.el.setWidth(width);
39002                 b.el.setHeight(height);
39003                 
39004             }, this);
39005             
39006             if(!box.length){
39007                 return;
39008             }
39009             
39010             var positions = [];
39011             
39012             switch (box.length){
39013                 case 1 :
39014                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39015                     break;
39016                 case 2 :
39017                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39018                     break;
39019                 case 3 :
39020                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39021                     break;
39022                 case 4 :
39023                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39024                     break;
39025                 default :
39026                     break;
39027             }
39028             
39029             Roo.each(box, function(b,kk){
39030                 
39031                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39032                 
39033                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39034                 
39035             }, this);
39036             
39037         }, this);
39038         
39039     },
39040     
39041     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39042     {
39043         Roo.each(eItems, function(b,k){
39044             
39045             b.size = (k == 0) ? 'sm' : 'xs';
39046             b.x = (k == 0) ? 2 : 1;
39047             b.y = (k == 0) ? 2 : 1;
39048             
39049             b.el.position('absolute');
39050             
39051             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39052                 
39053             b.el.setWidth(width);
39054             
39055             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39056             
39057             b.el.setHeight(height);
39058             
39059         }, this);
39060
39061         var positions = [];
39062         
39063         positions.push({
39064             x : maxX - this.unitWidth * 2 - this.gutter,
39065             y : minY
39066         });
39067         
39068         positions.push({
39069             x : maxX - this.unitWidth,
39070             y : minY + (this.unitWidth + this.gutter) * 2
39071         });
39072         
39073         positions.push({
39074             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39075             y : minY
39076         });
39077         
39078         Roo.each(eItems, function(b,k){
39079             
39080             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39081
39082         }, this);
39083         
39084     },
39085     
39086     getVerticalOneBoxColPositions : function(x, y, box)
39087     {
39088         var pos = [];
39089         
39090         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39091         
39092         if(box[0].size == 'md-left'){
39093             rand = 0;
39094         }
39095         
39096         if(box[0].size == 'md-right'){
39097             rand = 1;
39098         }
39099         
39100         pos.push({
39101             x : x + (this.unitWidth + this.gutter) * rand,
39102             y : y
39103         });
39104         
39105         return pos;
39106     },
39107     
39108     getVerticalTwoBoxColPositions : function(x, y, box)
39109     {
39110         var pos = [];
39111         
39112         if(box[0].size == 'xs'){
39113             
39114             pos.push({
39115                 x : x,
39116                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39117             });
39118
39119             pos.push({
39120                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39121                 y : y
39122             });
39123             
39124             return pos;
39125             
39126         }
39127         
39128         pos.push({
39129             x : x,
39130             y : y
39131         });
39132
39133         pos.push({
39134             x : x + (this.unitWidth + this.gutter) * 2,
39135             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39136         });
39137         
39138         return pos;
39139         
39140     },
39141     
39142     getVerticalThreeBoxColPositions : function(x, y, box)
39143     {
39144         var pos = [];
39145         
39146         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39147             
39148             pos.push({
39149                 x : x,
39150                 y : y
39151             });
39152
39153             pos.push({
39154                 x : x + (this.unitWidth + this.gutter) * 1,
39155                 y : y
39156             });
39157             
39158             pos.push({
39159                 x : x + (this.unitWidth + this.gutter) * 2,
39160                 y : y
39161             });
39162             
39163             return pos;
39164             
39165         }
39166         
39167         if(box[0].size == 'xs' && box[1].size == 'xs'){
39168             
39169             pos.push({
39170                 x : x,
39171                 y : y
39172             });
39173
39174             pos.push({
39175                 x : x,
39176                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39177             });
39178             
39179             pos.push({
39180                 x : x + (this.unitWidth + this.gutter) * 1,
39181                 y : y
39182             });
39183             
39184             return pos;
39185             
39186         }
39187         
39188         pos.push({
39189             x : x,
39190             y : y
39191         });
39192
39193         pos.push({
39194             x : x + (this.unitWidth + this.gutter) * 2,
39195             y : y
39196         });
39197
39198         pos.push({
39199             x : x + (this.unitWidth + this.gutter) * 2,
39200             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39201         });
39202             
39203         return pos;
39204         
39205     },
39206     
39207     getVerticalFourBoxColPositions : function(x, y, box)
39208     {
39209         var pos = [];
39210         
39211         if(box[0].size == 'xs'){
39212             
39213             pos.push({
39214                 x : x,
39215                 y : y
39216             });
39217
39218             pos.push({
39219                 x : x,
39220                 y : y + (this.unitHeight + this.gutter) * 1
39221             });
39222             
39223             pos.push({
39224                 x : x,
39225                 y : y + (this.unitHeight + this.gutter) * 2
39226             });
39227             
39228             pos.push({
39229                 x : x + (this.unitWidth + this.gutter) * 1,
39230                 y : y
39231             });
39232             
39233             return pos;
39234             
39235         }
39236         
39237         pos.push({
39238             x : x,
39239             y : y
39240         });
39241
39242         pos.push({
39243             x : x + (this.unitWidth + this.gutter) * 2,
39244             y : y
39245         });
39246
39247         pos.push({
39248             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39249             y : y + (this.unitHeight + this.gutter) * 1
39250         });
39251
39252         pos.push({
39253             x : x + (this.unitWidth + this.gutter) * 2,
39254             y : y + (this.unitWidth + this.gutter) * 2
39255         });
39256
39257         return pos;
39258         
39259     },
39260     
39261     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39262     {
39263         var pos = [];
39264         
39265         if(box[0].size == 'md-left'){
39266             pos.push({
39267                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39268                 y : minY
39269             });
39270             
39271             return pos;
39272         }
39273         
39274         if(box[0].size == 'md-right'){
39275             pos.push({
39276                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39277                 y : minY + (this.unitWidth + this.gutter) * 1
39278             });
39279             
39280             return pos;
39281         }
39282         
39283         var rand = Math.floor(Math.random() * (4 - box[0].y));
39284         
39285         pos.push({
39286             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39287             y : minY + (this.unitWidth + this.gutter) * rand
39288         });
39289         
39290         return pos;
39291         
39292     },
39293     
39294     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39295     {
39296         var pos = [];
39297         
39298         if(box[0].size == 'xs'){
39299             
39300             pos.push({
39301                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39302                 y : minY
39303             });
39304
39305             pos.push({
39306                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39307                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39308             });
39309             
39310             return pos;
39311             
39312         }
39313         
39314         pos.push({
39315             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39316             y : minY
39317         });
39318
39319         pos.push({
39320             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39321             y : minY + (this.unitWidth + this.gutter) * 2
39322         });
39323         
39324         return pos;
39325         
39326     },
39327     
39328     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39329     {
39330         var pos = [];
39331         
39332         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39333             
39334             pos.push({
39335                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39336                 y : minY
39337             });
39338
39339             pos.push({
39340                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39341                 y : minY + (this.unitWidth + this.gutter) * 1
39342             });
39343             
39344             pos.push({
39345                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39346                 y : minY + (this.unitWidth + this.gutter) * 2
39347             });
39348             
39349             return pos;
39350             
39351         }
39352         
39353         if(box[0].size == 'xs' && box[1].size == 'xs'){
39354             
39355             pos.push({
39356                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39357                 y : minY
39358             });
39359
39360             pos.push({
39361                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39362                 y : minY
39363             });
39364             
39365             pos.push({
39366                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39367                 y : minY + (this.unitWidth + this.gutter) * 1
39368             });
39369             
39370             return pos;
39371             
39372         }
39373         
39374         pos.push({
39375             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39376             y : minY
39377         });
39378
39379         pos.push({
39380             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39381             y : minY + (this.unitWidth + this.gutter) * 2
39382         });
39383
39384         pos.push({
39385             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39386             y : minY + (this.unitWidth + this.gutter) * 2
39387         });
39388             
39389         return pos;
39390         
39391     },
39392     
39393     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39394     {
39395         var pos = [];
39396         
39397         if(box[0].size == 'xs'){
39398             
39399             pos.push({
39400                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39401                 y : minY
39402             });
39403
39404             pos.push({
39405                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39406                 y : minY
39407             });
39408             
39409             pos.push({
39410                 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),
39411                 y : minY
39412             });
39413             
39414             pos.push({
39415                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39416                 y : minY + (this.unitWidth + this.gutter) * 1
39417             });
39418             
39419             return pos;
39420             
39421         }
39422         
39423         pos.push({
39424             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39425             y : minY
39426         });
39427         
39428         pos.push({
39429             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39430             y : minY + (this.unitWidth + this.gutter) * 2
39431         });
39432         
39433         pos.push({
39434             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39435             y : minY + (this.unitWidth + this.gutter) * 2
39436         });
39437         
39438         pos.push({
39439             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),
39440             y : minY + (this.unitWidth + this.gutter) * 2
39441         });
39442
39443         return pos;
39444         
39445     },
39446     
39447     /**
39448     * remove a Masonry Brick
39449     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39450     */
39451     removeBrick : function(brick_id)
39452     {
39453         if (!brick_id) {
39454             return;
39455         }
39456         
39457         for (var i = 0; i<this.bricks.length; i++) {
39458             if (this.bricks[i].id == brick_id) {
39459                 this.bricks.splice(i,1);
39460                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39461                 this.initial();
39462             }
39463         }
39464     },
39465     
39466     /**
39467     * adds a Masonry Brick
39468     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39469     */
39470     addBrick : function(cfg)
39471     {
39472         var cn = new Roo.bootstrap.MasonryBrick(cfg);
39473         //this.register(cn);
39474         cn.parentId = this.id;
39475         cn.render(this.el);
39476         return cn;
39477     },
39478     
39479     /**
39480     * register a Masonry Brick
39481     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39482     */
39483     
39484     register : function(brick)
39485     {
39486         this.bricks.push(brick);
39487         brick.masonryId = this.id;
39488     },
39489     
39490     /**
39491     * clear all the Masonry Brick
39492     */
39493     clearAll : function()
39494     {
39495         this.bricks = [];
39496         //this.getChildContainer().dom.innerHTML = "";
39497         this.el.dom.innerHTML = '';
39498     },
39499     
39500     getSelected : function()
39501     {
39502         if (!this.selectedBrick) {
39503             return false;
39504         }
39505         
39506         return this.selectedBrick;
39507     }
39508 });
39509
39510 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39511     
39512     groups: {},
39513      /**
39514     * register a Masonry Layout
39515     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39516     */
39517     
39518     register : function(layout)
39519     {
39520         this.groups[layout.id] = layout;
39521     },
39522     /**
39523     * fetch a  Masonry Layout based on the masonry layout ID
39524     * @param {string} the masonry layout to add
39525     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39526     */
39527     
39528     get: function(layout_id) {
39529         if (typeof(this.groups[layout_id]) == 'undefined') {
39530             return false;
39531         }
39532         return this.groups[layout_id] ;
39533     }
39534     
39535     
39536     
39537 });
39538
39539  
39540
39541  /**
39542  *
39543  * This is based on 
39544  * http://masonry.desandro.com
39545  *
39546  * The idea is to render all the bricks based on vertical width...
39547  *
39548  * The original code extends 'outlayer' - we might need to use that....
39549  * 
39550  */
39551
39552
39553 /**
39554  * @class Roo.bootstrap.LayoutMasonryAuto
39555  * @extends Roo.bootstrap.Component
39556  * Bootstrap Layout Masonry class
39557  * 
39558  * @constructor
39559  * Create a new Element
39560  * @param {Object} config The config object
39561  */
39562
39563 Roo.bootstrap.LayoutMasonryAuto = function(config){
39564     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39565 };
39566
39567 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
39568     
39569       /**
39570      * @cfg {Boolean} isFitWidth  - resize the width..
39571      */   
39572     isFitWidth : false,  // options..
39573     /**
39574      * @cfg {Boolean} isOriginLeft = left align?
39575      */   
39576     isOriginLeft : true,
39577     /**
39578      * @cfg {Boolean} isOriginTop = top align?
39579      */   
39580     isOriginTop : false,
39581     /**
39582      * @cfg {Boolean} isLayoutInstant = no animation?
39583      */   
39584     isLayoutInstant : false, // needed?
39585     /**
39586      * @cfg {Boolean} isResizingContainer = not sure if this is used..
39587      */   
39588     isResizingContainer : true,
39589     /**
39590      * @cfg {Number} columnWidth  width of the columns 
39591      */   
39592     
39593     columnWidth : 0,
39594     
39595     /**
39596      * @cfg {Number} maxCols maximum number of columns
39597      */   
39598     
39599     maxCols: 0,
39600     /**
39601      * @cfg {Number} padHeight padding below box..
39602      */   
39603     
39604     padHeight : 10, 
39605     
39606     /**
39607      * @cfg {Boolean} isAutoInitial defalut true
39608      */   
39609     
39610     isAutoInitial : true, 
39611     
39612     // private?
39613     gutter : 0,
39614     
39615     containerWidth: 0,
39616     initialColumnWidth : 0,
39617     currentSize : null,
39618     
39619     colYs : null, // array.
39620     maxY : 0,
39621     padWidth: 10,
39622     
39623     
39624     tag: 'div',
39625     cls: '',
39626     bricks: null, //CompositeElement
39627     cols : 0, // array?
39628     // element : null, // wrapped now this.el
39629     _isLayoutInited : null, 
39630     
39631     
39632     getAutoCreate : function(){
39633         
39634         var cfg = {
39635             tag: this.tag,
39636             cls: 'blog-masonary-wrapper ' + this.cls,
39637             cn : {
39638                 cls : 'mas-boxes masonary'
39639             }
39640         };
39641         
39642         return cfg;
39643     },
39644     
39645     getChildContainer: function( )
39646     {
39647         if (this.boxesEl) {
39648             return this.boxesEl;
39649         }
39650         
39651         this.boxesEl = this.el.select('.mas-boxes').first();
39652         
39653         return this.boxesEl;
39654     },
39655     
39656     
39657     initEvents : function()
39658     {
39659         var _this = this;
39660         
39661         if(this.isAutoInitial){
39662             Roo.log('hook children rendered');
39663             this.on('childrenrendered', function() {
39664                 Roo.log('children rendered');
39665                 _this.initial();
39666             } ,this);
39667         }
39668         
39669     },
39670     
39671     initial : function()
39672     {
39673         this.reloadItems();
39674
39675         this.currentSize = this.el.getBox(true);
39676
39677         /// was window resize... - let's see if this works..
39678         Roo.EventManager.onWindowResize(this.resize, this); 
39679
39680         if(!this.isAutoInitial){
39681             this.layout();
39682             return;
39683         }
39684         
39685         this.layout.defer(500,this);
39686     },
39687     
39688     reloadItems: function()
39689     {
39690         this.bricks = this.el.select('.masonry-brick', true);
39691         
39692         this.bricks.each(function(b) {
39693             //Roo.log(b.getSize());
39694             if (!b.attr('originalwidth')) {
39695                 b.attr('originalwidth',  b.getSize().width);
39696             }
39697             
39698         });
39699         
39700         Roo.log(this.bricks.elements.length);
39701     },
39702     
39703     resize : function()
39704     {
39705         Roo.log('resize');
39706         var cs = this.el.getBox(true);
39707         
39708         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
39709             Roo.log("no change in with or X");
39710             return;
39711         }
39712         this.currentSize = cs;
39713         this.layout();
39714     },
39715     
39716     layout : function()
39717     {
39718          Roo.log('layout');
39719         this._resetLayout();
39720         //this._manageStamps();
39721       
39722         // don't animate first layout
39723         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39724         this.layoutItems( isInstant );
39725       
39726         // flag for initalized
39727         this._isLayoutInited = true;
39728     },
39729     
39730     layoutItems : function( isInstant )
39731     {
39732         //var items = this._getItemsForLayout( this.items );
39733         // original code supports filtering layout items.. we just ignore it..
39734         
39735         this._layoutItems( this.bricks , isInstant );
39736       
39737         this._postLayout();
39738     },
39739     _layoutItems : function ( items , isInstant)
39740     {
39741        //this.fireEvent( 'layout', this, items );
39742     
39743
39744         if ( !items || !items.elements.length ) {
39745           // no items, emit event with empty array
39746             return;
39747         }
39748
39749         var queue = [];
39750         items.each(function(item) {
39751             Roo.log("layout item");
39752             Roo.log(item);
39753             // get x/y object from method
39754             var position = this._getItemLayoutPosition( item );
39755             // enqueue
39756             position.item = item;
39757             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
39758             queue.push( position );
39759         }, this);
39760       
39761         this._processLayoutQueue( queue );
39762     },
39763     /** Sets position of item in DOM
39764     * @param {Element} item
39765     * @param {Number} x - horizontal position
39766     * @param {Number} y - vertical position
39767     * @param {Boolean} isInstant - disables transitions
39768     */
39769     _processLayoutQueue : function( queue )
39770     {
39771         for ( var i=0, len = queue.length; i < len; i++ ) {
39772             var obj = queue[i];
39773             obj.item.position('absolute');
39774             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
39775         }
39776     },
39777       
39778     
39779     /**
39780     * Any logic you want to do after each layout,
39781     * i.e. size the container
39782     */
39783     _postLayout : function()
39784     {
39785         this.resizeContainer();
39786     },
39787     
39788     resizeContainer : function()
39789     {
39790         if ( !this.isResizingContainer ) {
39791             return;
39792         }
39793         var size = this._getContainerSize();
39794         if ( size ) {
39795             this.el.setSize(size.width,size.height);
39796             this.boxesEl.setSize(size.width,size.height);
39797         }
39798     },
39799     
39800     
39801     
39802     _resetLayout : function()
39803     {
39804         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
39805         this.colWidth = this.el.getWidth();
39806         //this.gutter = this.el.getWidth(); 
39807         
39808         this.measureColumns();
39809
39810         // reset column Y
39811         var i = this.cols;
39812         this.colYs = [];
39813         while (i--) {
39814             this.colYs.push( 0 );
39815         }
39816     
39817         this.maxY = 0;
39818     },
39819
39820     measureColumns : function()
39821     {
39822         this.getContainerWidth();
39823       // if columnWidth is 0, default to outerWidth of first item
39824         if ( !this.columnWidth ) {
39825             var firstItem = this.bricks.first();
39826             Roo.log(firstItem);
39827             this.columnWidth  = this.containerWidth;
39828             if (firstItem && firstItem.attr('originalwidth') ) {
39829                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
39830             }
39831             // columnWidth fall back to item of first element
39832             Roo.log("set column width?");
39833                         this.initialColumnWidth = this.columnWidth  ;
39834
39835             // if first elem has no width, default to size of container
39836             
39837         }
39838         
39839         
39840         if (this.initialColumnWidth) {
39841             this.columnWidth = this.initialColumnWidth;
39842         }
39843         
39844         
39845             
39846         // column width is fixed at the top - however if container width get's smaller we should
39847         // reduce it...
39848         
39849         // this bit calcs how man columns..
39850             
39851         var columnWidth = this.columnWidth += this.gutter;
39852       
39853         // calculate columns
39854         var containerWidth = this.containerWidth + this.gutter;
39855         
39856         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
39857         // fix rounding errors, typically with gutters
39858         var excess = columnWidth - containerWidth % columnWidth;
39859         
39860         
39861         // if overshoot is less than a pixel, round up, otherwise floor it
39862         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
39863         cols = Math[ mathMethod ]( cols );
39864         this.cols = Math.max( cols, 1 );
39865         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39866         
39867          // padding positioning..
39868         var totalColWidth = this.cols * this.columnWidth;
39869         var padavail = this.containerWidth - totalColWidth;
39870         // so for 2 columns - we need 3 'pads'
39871         
39872         var padNeeded = (1+this.cols) * this.padWidth;
39873         
39874         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
39875         
39876         this.columnWidth += padExtra
39877         //this.padWidth = Math.floor(padavail /  ( this.cols));
39878         
39879         // adjust colum width so that padding is fixed??
39880         
39881         // we have 3 columns ... total = width * 3
39882         // we have X left over... that should be used by 
39883         
39884         //if (this.expandC) {
39885             
39886         //}
39887         
39888         
39889         
39890     },
39891     
39892     getContainerWidth : function()
39893     {
39894        /* // container is parent if fit width
39895         var container = this.isFitWidth ? this.element.parentNode : this.element;
39896         // check that this.size and size are there
39897         // IE8 triggers resize on body size change, so they might not be
39898         
39899         var size = getSize( container );  //FIXME
39900         this.containerWidth = size && size.innerWidth; //FIXME
39901         */
39902          
39903         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39904         
39905     },
39906     
39907     _getItemLayoutPosition : function( item )  // what is item?
39908     {
39909         // we resize the item to our columnWidth..
39910       
39911         item.setWidth(this.columnWidth);
39912         item.autoBoxAdjust  = false;
39913         
39914         var sz = item.getSize();
39915  
39916         // how many columns does this brick span
39917         var remainder = this.containerWidth % this.columnWidth;
39918         
39919         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
39920         // round if off by 1 pixel, otherwise use ceil
39921         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
39922         colSpan = Math.min( colSpan, this.cols );
39923         
39924         // normally this should be '1' as we dont' currently allow multi width columns..
39925         
39926         var colGroup = this._getColGroup( colSpan );
39927         // get the minimum Y value from the columns
39928         var minimumY = Math.min.apply( Math, colGroup );
39929         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39930         
39931         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
39932          
39933         // position the brick
39934         var position = {
39935             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
39936             y: this.currentSize.y + minimumY + this.padHeight
39937         };
39938         
39939         Roo.log(position);
39940         // apply setHeight to necessary columns
39941         var setHeight = minimumY + sz.height + this.padHeight;
39942         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39943         
39944         var setSpan = this.cols + 1 - colGroup.length;
39945         for ( var i = 0; i < setSpan; i++ ) {
39946           this.colYs[ shortColIndex + i ] = setHeight ;
39947         }
39948       
39949         return position;
39950     },
39951     
39952     /**
39953      * @param {Number} colSpan - number of columns the element spans
39954      * @returns {Array} colGroup
39955      */
39956     _getColGroup : function( colSpan )
39957     {
39958         if ( colSpan < 2 ) {
39959           // if brick spans only one column, use all the column Ys
39960           return this.colYs;
39961         }
39962       
39963         var colGroup = [];
39964         // how many different places could this brick fit horizontally
39965         var groupCount = this.cols + 1 - colSpan;
39966         // for each group potential horizontal position
39967         for ( var i = 0; i < groupCount; i++ ) {
39968           // make an array of colY values for that one group
39969           var groupColYs = this.colYs.slice( i, i + colSpan );
39970           // and get the max value of the array
39971           colGroup[i] = Math.max.apply( Math, groupColYs );
39972         }
39973         return colGroup;
39974     },
39975     /*
39976     _manageStamp : function( stamp )
39977     {
39978         var stampSize =  stamp.getSize();
39979         var offset = stamp.getBox();
39980         // get the columns that this stamp affects
39981         var firstX = this.isOriginLeft ? offset.x : offset.right;
39982         var lastX = firstX + stampSize.width;
39983         var firstCol = Math.floor( firstX / this.columnWidth );
39984         firstCol = Math.max( 0, firstCol );
39985         
39986         var lastCol = Math.floor( lastX / this.columnWidth );
39987         // lastCol should not go over if multiple of columnWidth #425
39988         lastCol -= lastX % this.columnWidth ? 0 : 1;
39989         lastCol = Math.min( this.cols - 1, lastCol );
39990         
39991         // set colYs to bottom of the stamp
39992         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
39993             stampSize.height;
39994             
39995         for ( var i = firstCol; i <= lastCol; i++ ) {
39996           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
39997         }
39998     },
39999     */
40000     
40001     _getContainerSize : function()
40002     {
40003         this.maxY = Math.max.apply( Math, this.colYs );
40004         var size = {
40005             height: this.maxY
40006         };
40007       
40008         if ( this.isFitWidth ) {
40009             size.width = this._getContainerFitWidth();
40010         }
40011       
40012         return size;
40013     },
40014     
40015     _getContainerFitWidth : function()
40016     {
40017         var unusedCols = 0;
40018         // count unused columns
40019         var i = this.cols;
40020         while ( --i ) {
40021           if ( this.colYs[i] !== 0 ) {
40022             break;
40023           }
40024           unusedCols++;
40025         }
40026         // fit container to columns that have been used
40027         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40028     },
40029     
40030     needsResizeLayout : function()
40031     {
40032         var previousWidth = this.containerWidth;
40033         this.getContainerWidth();
40034         return previousWidth !== this.containerWidth;
40035     }
40036  
40037 });
40038
40039  
40040
40041  /*
40042  * - LGPL
40043  *
40044  * element
40045  * 
40046  */
40047
40048 /**
40049  * @class Roo.bootstrap.MasonryBrick
40050  * @extends Roo.bootstrap.Component
40051  * Bootstrap MasonryBrick class
40052  * 
40053  * @constructor
40054  * Create a new MasonryBrick
40055  * @param {Object} config The config object
40056  */
40057
40058 Roo.bootstrap.MasonryBrick = function(config){
40059     
40060     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40061     
40062     Roo.bootstrap.MasonryBrick.register(this);
40063     
40064     this.addEvents({
40065         // raw events
40066         /**
40067          * @event click
40068          * When a MasonryBrick is clcik
40069          * @param {Roo.bootstrap.MasonryBrick} this
40070          * @param {Roo.EventObject} e
40071          */
40072         "click" : true
40073     });
40074 };
40075
40076 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40077     
40078     /**
40079      * @cfg {String} title
40080      */   
40081     title : '',
40082     /**
40083      * @cfg {String} html
40084      */   
40085     html : '',
40086     /**
40087      * @cfg {String} bgimage
40088      */   
40089     bgimage : '',
40090     /**
40091      * @cfg {String} videourl
40092      */   
40093     videourl : '',
40094     /**
40095      * @cfg {String} cls
40096      */   
40097     cls : '',
40098     /**
40099      * @cfg {String} href
40100      */   
40101     href : '',
40102     /**
40103      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40104      */   
40105     size : 'xs',
40106     
40107     /**
40108      * @cfg {String} placetitle (center|bottom)
40109      */   
40110     placetitle : '',
40111     
40112     /**
40113      * @cfg {Boolean} isFitContainer defalut true
40114      */   
40115     isFitContainer : true, 
40116     
40117     /**
40118      * @cfg {Boolean} preventDefault defalut false
40119      */   
40120     preventDefault : false, 
40121     
40122     /**
40123      * @cfg {Boolean} inverse defalut false
40124      */   
40125     maskInverse : false, 
40126     
40127     getAutoCreate : function()
40128     {
40129         if(!this.isFitContainer){
40130             return this.getSplitAutoCreate();
40131         }
40132         
40133         var cls = 'masonry-brick masonry-brick-full';
40134         
40135         if(this.href.length){
40136             cls += ' masonry-brick-link';
40137         }
40138         
40139         if(this.bgimage.length){
40140             cls += ' masonry-brick-image';
40141         }
40142         
40143         if(this.maskInverse){
40144             cls += ' mask-inverse';
40145         }
40146         
40147         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40148             cls += ' enable-mask';
40149         }
40150         
40151         if(this.size){
40152             cls += ' masonry-' + this.size + '-brick';
40153         }
40154         
40155         if(this.placetitle.length){
40156             
40157             switch (this.placetitle) {
40158                 case 'center' :
40159                     cls += ' masonry-center-title';
40160                     break;
40161                 case 'bottom' :
40162                     cls += ' masonry-bottom-title';
40163                     break;
40164                 default:
40165                     break;
40166             }
40167             
40168         } else {
40169             if(!this.html.length && !this.bgimage.length){
40170                 cls += ' masonry-center-title';
40171             }
40172
40173             if(!this.html.length && this.bgimage.length){
40174                 cls += ' masonry-bottom-title';
40175             }
40176         }
40177         
40178         if(this.cls){
40179             cls += ' ' + this.cls;
40180         }
40181         
40182         var cfg = {
40183             tag: (this.href.length) ? 'a' : 'div',
40184             cls: cls,
40185             cn: [
40186                 {
40187                     tag: 'div',
40188                     cls: 'masonry-brick-mask'
40189                 },
40190                 {
40191                     tag: 'div',
40192                     cls: 'masonry-brick-paragraph',
40193                     cn: []
40194                 }
40195             ]
40196         };
40197         
40198         if(this.href.length){
40199             cfg.href = this.href;
40200         }
40201         
40202         var cn = cfg.cn[1].cn;
40203         
40204         if(this.title.length){
40205             cn.push({
40206                 tag: 'h4',
40207                 cls: 'masonry-brick-title',
40208                 html: this.title
40209             });
40210         }
40211         
40212         if(this.html.length){
40213             cn.push({
40214                 tag: 'p',
40215                 cls: 'masonry-brick-text',
40216                 html: this.html
40217             });
40218         }
40219         
40220         if (!this.title.length && !this.html.length) {
40221             cfg.cn[1].cls += ' hide';
40222         }
40223         
40224         if(this.bgimage.length){
40225             cfg.cn.push({
40226                 tag: 'img',
40227                 cls: 'masonry-brick-image-view',
40228                 src: this.bgimage
40229             });
40230         }
40231         
40232         if(this.videourl.length){
40233             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40234             // youtube support only?
40235             cfg.cn.push({
40236                 tag: 'iframe',
40237                 cls: 'masonry-brick-image-view',
40238                 src: vurl,
40239                 frameborder : 0,
40240                 allowfullscreen : true
40241             });
40242         }
40243         
40244         return cfg;
40245         
40246     },
40247     
40248     getSplitAutoCreate : function()
40249     {
40250         var cls = 'masonry-brick masonry-brick-split';
40251         
40252         if(this.href.length){
40253             cls += ' masonry-brick-link';
40254         }
40255         
40256         if(this.bgimage.length){
40257             cls += ' masonry-brick-image';
40258         }
40259         
40260         if(this.size){
40261             cls += ' masonry-' + this.size + '-brick';
40262         }
40263         
40264         switch (this.placetitle) {
40265             case 'center' :
40266                 cls += ' masonry-center-title';
40267                 break;
40268             case 'bottom' :
40269                 cls += ' masonry-bottom-title';
40270                 break;
40271             default:
40272                 if(!this.bgimage.length){
40273                     cls += ' masonry-center-title';
40274                 }
40275
40276                 if(this.bgimage.length){
40277                     cls += ' masonry-bottom-title';
40278                 }
40279                 break;
40280         }
40281         
40282         if(this.cls){
40283             cls += ' ' + this.cls;
40284         }
40285         
40286         var cfg = {
40287             tag: (this.href.length) ? 'a' : 'div',
40288             cls: cls,
40289             cn: [
40290                 {
40291                     tag: 'div',
40292                     cls: 'masonry-brick-split-head',
40293                     cn: [
40294                         {
40295                             tag: 'div',
40296                             cls: 'masonry-brick-paragraph',
40297                             cn: []
40298                         }
40299                     ]
40300                 },
40301                 {
40302                     tag: 'div',
40303                     cls: 'masonry-brick-split-body',
40304                     cn: []
40305                 }
40306             ]
40307         };
40308         
40309         if(this.href.length){
40310             cfg.href = this.href;
40311         }
40312         
40313         if(this.title.length){
40314             cfg.cn[0].cn[0].cn.push({
40315                 tag: 'h4',
40316                 cls: 'masonry-brick-title',
40317                 html: this.title
40318             });
40319         }
40320         
40321         if(this.html.length){
40322             cfg.cn[1].cn.push({
40323                 tag: 'p',
40324                 cls: 'masonry-brick-text',
40325                 html: this.html
40326             });
40327         }
40328
40329         if(this.bgimage.length){
40330             cfg.cn[0].cn.push({
40331                 tag: 'img',
40332                 cls: 'masonry-brick-image-view',
40333                 src: this.bgimage
40334             });
40335         }
40336         
40337         if(this.videourl.length){
40338             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40339             // youtube support only?
40340             cfg.cn[0].cn.cn.push({
40341                 tag: 'iframe',
40342                 cls: 'masonry-brick-image-view',
40343                 src: vurl,
40344                 frameborder : 0,
40345                 allowfullscreen : true
40346             });
40347         }
40348         
40349         return cfg;
40350     },
40351     
40352     initEvents: function() 
40353     {
40354         switch (this.size) {
40355             case 'xs' :
40356                 this.x = 1;
40357                 this.y = 1;
40358                 break;
40359             case 'sm' :
40360                 this.x = 2;
40361                 this.y = 2;
40362                 break;
40363             case 'md' :
40364             case 'md-left' :
40365             case 'md-right' :
40366                 this.x = 3;
40367                 this.y = 3;
40368                 break;
40369             case 'tall' :
40370                 this.x = 2;
40371                 this.y = 3;
40372                 break;
40373             case 'wide' :
40374                 this.x = 3;
40375                 this.y = 2;
40376                 break;
40377             case 'wide-thin' :
40378                 this.x = 3;
40379                 this.y = 1;
40380                 break;
40381                         
40382             default :
40383                 break;
40384         }
40385         
40386         if(Roo.isTouch){
40387             this.el.on('touchstart', this.onTouchStart, this);
40388             this.el.on('touchmove', this.onTouchMove, this);
40389             this.el.on('touchend', this.onTouchEnd, this);
40390             this.el.on('contextmenu', this.onContextMenu, this);
40391         } else {
40392             this.el.on('mouseenter'  ,this.enter, this);
40393             this.el.on('mouseleave', this.leave, this);
40394             this.el.on('click', this.onClick, this);
40395         }
40396         
40397         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40398             this.parent().bricks.push(this);   
40399         }
40400         
40401     },
40402     
40403     onClick: function(e, el)
40404     {
40405         var time = this.endTimer - this.startTimer;
40406         // Roo.log(e.preventDefault());
40407         if(Roo.isTouch){
40408             if(time > 1000){
40409                 e.preventDefault();
40410                 return;
40411             }
40412         }
40413         
40414         if(!this.preventDefault){
40415             return;
40416         }
40417         
40418         e.preventDefault();
40419         
40420         if (this.activeClass != '') {
40421             this.selectBrick();
40422         }
40423         
40424         this.fireEvent('click', this, e);
40425     },
40426     
40427     enter: function(e, el)
40428     {
40429         e.preventDefault();
40430         
40431         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40432             return;
40433         }
40434         
40435         if(this.bgimage.length && this.html.length){
40436             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40437         }
40438     },
40439     
40440     leave: function(e, el)
40441     {
40442         e.preventDefault();
40443         
40444         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40445             return;
40446         }
40447         
40448         if(this.bgimage.length && this.html.length){
40449             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40450         }
40451     },
40452     
40453     onTouchStart: function(e, el)
40454     {
40455 //        e.preventDefault();
40456         
40457         this.touchmoved = false;
40458         
40459         if(!this.isFitContainer){
40460             return;
40461         }
40462         
40463         if(!this.bgimage.length || !this.html.length){
40464             return;
40465         }
40466         
40467         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40468         
40469         this.timer = new Date().getTime();
40470         
40471     },
40472     
40473     onTouchMove: function(e, el)
40474     {
40475         this.touchmoved = true;
40476     },
40477     
40478     onContextMenu : function(e,el)
40479     {
40480         e.preventDefault();
40481         e.stopPropagation();
40482         return false;
40483     },
40484     
40485     onTouchEnd: function(e, el)
40486     {
40487 //        e.preventDefault();
40488         
40489         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40490         
40491             this.leave(e,el);
40492             
40493             return;
40494         }
40495         
40496         if(!this.bgimage.length || !this.html.length){
40497             
40498             if(this.href.length){
40499                 window.location.href = this.href;
40500             }
40501             
40502             return;
40503         }
40504         
40505         if(!this.isFitContainer){
40506             return;
40507         }
40508         
40509         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40510         
40511         window.location.href = this.href;
40512     },
40513     
40514     //selection on single brick only
40515     selectBrick : function() {
40516         
40517         if (!this.parentId) {
40518             return;
40519         }
40520         
40521         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40522         var index = m.selectedBrick.indexOf(this.id);
40523         
40524         if ( index > -1) {
40525             m.selectedBrick.splice(index,1);
40526             this.el.removeClass(this.activeClass);
40527             return;
40528         }
40529         
40530         for(var i = 0; i < m.selectedBrick.length; i++) {
40531             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40532             b.el.removeClass(b.activeClass);
40533         }
40534         
40535         m.selectedBrick = [];
40536         
40537         m.selectedBrick.push(this.id);
40538         this.el.addClass(this.activeClass);
40539         return;
40540     },
40541     
40542     isSelected : function(){
40543         return this.el.hasClass(this.activeClass);
40544         
40545     }
40546 });
40547
40548 Roo.apply(Roo.bootstrap.MasonryBrick, {
40549     
40550     //groups: {},
40551     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40552      /**
40553     * register a Masonry Brick
40554     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40555     */
40556     
40557     register : function(brick)
40558     {
40559         //this.groups[brick.id] = brick;
40560         this.groups.add(brick.id, brick);
40561     },
40562     /**
40563     * fetch a  masonry brick based on the masonry brick ID
40564     * @param {string} the masonry brick to add
40565     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
40566     */
40567     
40568     get: function(brick_id) 
40569     {
40570         // if (typeof(this.groups[brick_id]) == 'undefined') {
40571         //     return false;
40572         // }
40573         // return this.groups[brick_id] ;
40574         
40575         if(this.groups.key(brick_id)) {
40576             return this.groups.key(brick_id);
40577         }
40578         
40579         return false;
40580     }
40581     
40582     
40583     
40584 });
40585
40586  /*
40587  * - LGPL
40588  *
40589  * element
40590  * 
40591  */
40592
40593 /**
40594  * @class Roo.bootstrap.Brick
40595  * @extends Roo.bootstrap.Component
40596  * Bootstrap Brick class
40597  * 
40598  * @constructor
40599  * Create a new Brick
40600  * @param {Object} config The config object
40601  */
40602
40603 Roo.bootstrap.Brick = function(config){
40604     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
40605     
40606     this.addEvents({
40607         // raw events
40608         /**
40609          * @event click
40610          * When a Brick is click
40611          * @param {Roo.bootstrap.Brick} this
40612          * @param {Roo.EventObject} e
40613          */
40614         "click" : true
40615     });
40616 };
40617
40618 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
40619     
40620     /**
40621      * @cfg {String} title
40622      */   
40623     title : '',
40624     /**
40625      * @cfg {String} html
40626      */   
40627     html : '',
40628     /**
40629      * @cfg {String} bgimage
40630      */   
40631     bgimage : '',
40632     /**
40633      * @cfg {String} cls
40634      */   
40635     cls : '',
40636     /**
40637      * @cfg {String} href
40638      */   
40639     href : '',
40640     /**
40641      * @cfg {String} video
40642      */   
40643     video : '',
40644     /**
40645      * @cfg {Boolean} square
40646      */   
40647     square : true,
40648     
40649     getAutoCreate : function()
40650     {
40651         var cls = 'roo-brick';
40652         
40653         if(this.href.length){
40654             cls += ' roo-brick-link';
40655         }
40656         
40657         if(this.bgimage.length){
40658             cls += ' roo-brick-image';
40659         }
40660         
40661         if(!this.html.length && !this.bgimage.length){
40662             cls += ' roo-brick-center-title';
40663         }
40664         
40665         if(!this.html.length && this.bgimage.length){
40666             cls += ' roo-brick-bottom-title';
40667         }
40668         
40669         if(this.cls){
40670             cls += ' ' + this.cls;
40671         }
40672         
40673         var cfg = {
40674             tag: (this.href.length) ? 'a' : 'div',
40675             cls: cls,
40676             cn: [
40677                 {
40678                     tag: 'div',
40679                     cls: 'roo-brick-paragraph',
40680                     cn: []
40681                 }
40682             ]
40683         };
40684         
40685         if(this.href.length){
40686             cfg.href = this.href;
40687         }
40688         
40689         var cn = cfg.cn[0].cn;
40690         
40691         if(this.title.length){
40692             cn.push({
40693                 tag: 'h4',
40694                 cls: 'roo-brick-title',
40695                 html: this.title
40696             });
40697         }
40698         
40699         if(this.html.length){
40700             cn.push({
40701                 tag: 'p',
40702                 cls: 'roo-brick-text',
40703                 html: this.html
40704             });
40705         } else {
40706             cn.cls += ' hide';
40707         }
40708         
40709         if(this.bgimage.length){
40710             cfg.cn.push({
40711                 tag: 'img',
40712                 cls: 'roo-brick-image-view',
40713                 src: this.bgimage
40714             });
40715         }
40716         
40717         return cfg;
40718     },
40719     
40720     initEvents: function() 
40721     {
40722         if(this.title.length || this.html.length){
40723             this.el.on('mouseenter'  ,this.enter, this);
40724             this.el.on('mouseleave', this.leave, this);
40725         }
40726         
40727         Roo.EventManager.onWindowResize(this.resize, this); 
40728         
40729         if(this.bgimage.length){
40730             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
40731             this.imageEl.on('load', this.onImageLoad, this);
40732             return;
40733         }
40734         
40735         this.resize();
40736     },
40737     
40738     onImageLoad : function()
40739     {
40740         this.resize();
40741     },
40742     
40743     resize : function()
40744     {
40745         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
40746         
40747         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
40748         
40749         if(this.bgimage.length){
40750             var image = this.el.select('.roo-brick-image-view', true).first();
40751             
40752             image.setWidth(paragraph.getWidth());
40753             
40754             if(this.square){
40755                 image.setHeight(paragraph.getWidth());
40756             }
40757             
40758             this.el.setHeight(image.getHeight());
40759             paragraph.setHeight(image.getHeight());
40760             
40761         }
40762         
40763     },
40764     
40765     enter: function(e, el)
40766     {
40767         e.preventDefault();
40768         
40769         if(this.bgimage.length){
40770             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
40771             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
40772         }
40773     },
40774     
40775     leave: function(e, el)
40776     {
40777         e.preventDefault();
40778         
40779         if(this.bgimage.length){
40780             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
40781             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
40782         }
40783     }
40784     
40785 });
40786
40787  
40788
40789  /*
40790  * - LGPL
40791  *
40792  * Number field 
40793  */
40794
40795 /**
40796  * @class Roo.bootstrap.form.NumberField
40797  * @extends Roo.bootstrap.form.Input
40798  * Bootstrap NumberField class
40799  * 
40800  * 
40801  * 
40802  * 
40803  * @constructor
40804  * Create a new NumberField
40805  * @param {Object} config The config object
40806  */
40807
40808 Roo.bootstrap.form.NumberField = function(config){
40809     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
40810 };
40811
40812 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
40813     
40814     /**
40815      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40816      */
40817     allowDecimals : true,
40818     /**
40819      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40820      */
40821     decimalSeparator : ".",
40822     /**
40823      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40824      */
40825     decimalPrecision : 2,
40826     /**
40827      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40828      */
40829     allowNegative : true,
40830     
40831     /**
40832      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40833      */
40834     allowZero: true,
40835     /**
40836      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40837      */
40838     minValue : Number.NEGATIVE_INFINITY,
40839     /**
40840      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40841      */
40842     maxValue : Number.MAX_VALUE,
40843     /**
40844      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40845      */
40846     minText : "The minimum value for this field is {0}",
40847     /**
40848      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40849      */
40850     maxText : "The maximum value for this field is {0}",
40851     /**
40852      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40853      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40854      */
40855     nanText : "{0} is not a valid number",
40856     /**
40857      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40858      */
40859     thousandsDelimiter : false,
40860     /**
40861      * @cfg {String} valueAlign alignment of value
40862      */
40863     valueAlign : "left",
40864
40865     getAutoCreate : function()
40866     {
40867         var hiddenInput = {
40868             tag: 'input',
40869             type: 'hidden',
40870             id: Roo.id(),
40871             cls: 'hidden-number-input'
40872         };
40873         
40874         if (this.name) {
40875             hiddenInput.name = this.name;
40876         }
40877         
40878         this.name = '';
40879         
40880         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
40881         
40882         this.name = hiddenInput.name;
40883         
40884         if(cfg.cn.length > 0) {
40885             cfg.cn.push(hiddenInput);
40886         }
40887         
40888         return cfg;
40889     },
40890
40891     // private
40892     initEvents : function()
40893     {   
40894         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
40895         
40896         var allowed = "0123456789";
40897         
40898         if(this.allowDecimals){
40899             allowed += this.decimalSeparator;
40900         }
40901         
40902         if(this.allowNegative){
40903             allowed += "-";
40904         }
40905         
40906         if(this.thousandsDelimiter) {
40907             allowed += ",";
40908         }
40909         
40910         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40911         
40912         var keyPress = function(e){
40913             
40914             var k = e.getKey();
40915             
40916             var c = e.getCharCode();
40917             
40918             if(
40919                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40920                     allowed.indexOf(String.fromCharCode(c)) === -1
40921             ){
40922                 e.stopEvent();
40923                 return;
40924             }
40925             
40926             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40927                 return;
40928             }
40929             
40930             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40931                 e.stopEvent();
40932             }
40933         };
40934         
40935         this.el.on("keypress", keyPress, this);
40936     },
40937     
40938     validateValue : function(value)
40939     {
40940         
40941         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
40942             return false;
40943         }
40944         
40945         var num = this.parseValue(value);
40946         
40947         if(isNaN(num)){
40948             this.markInvalid(String.format(this.nanText, value));
40949             return false;
40950         }
40951         
40952         if(num < this.minValue){
40953             this.markInvalid(String.format(this.minText, this.minValue));
40954             return false;
40955         }
40956         
40957         if(num > this.maxValue){
40958             this.markInvalid(String.format(this.maxText, this.maxValue));
40959             return false;
40960         }
40961         
40962         return true;
40963     },
40964
40965     getValue : function()
40966     {
40967         var v = this.hiddenEl().getValue();
40968         
40969         return this.fixPrecision(this.parseValue(v));
40970     },
40971
40972     parseValue : function(value)
40973     {
40974         if(this.thousandsDelimiter) {
40975             value += "";
40976             r = new RegExp(",", "g");
40977             value = value.replace(r, "");
40978         }
40979         
40980         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40981         return isNaN(value) ? '' : value;
40982     },
40983
40984     fixPrecision : function(value)
40985     {
40986         if(this.thousandsDelimiter) {
40987             value += "";
40988             r = new RegExp(",", "g");
40989             value = value.replace(r, "");
40990         }
40991         
40992         var nan = isNaN(value);
40993         
40994         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40995             return nan ? '' : value;
40996         }
40997         return parseFloat(value).toFixed(this.decimalPrecision);
40998     },
40999
41000     setValue : function(v)
41001     {
41002         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41003         
41004         this.value = v;
41005         
41006         if(this.rendered){
41007             
41008             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41009             
41010             this.inputEl().dom.value = (v == '') ? '' :
41011                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41012             
41013             if(!this.allowZero && v === '0') {
41014                 this.hiddenEl().dom.value = '';
41015                 this.inputEl().dom.value = '';
41016             }
41017             
41018             this.validate();
41019         }
41020     },
41021
41022     decimalPrecisionFcn : function(v)
41023     {
41024         return Math.floor(v);
41025     },
41026
41027     beforeBlur : function()
41028     {
41029         var v = this.parseValue(this.getRawValue());
41030         
41031         if(v || v === 0 || v === ''){
41032             this.setValue(v);
41033         }
41034     },
41035     
41036     hiddenEl : function()
41037     {
41038         return this.el.select('input.hidden-number-input',true).first();
41039     }
41040     
41041 });
41042
41043  
41044
41045 /*
41046 * Licence: LGPL
41047 */
41048
41049 /**
41050  * @class Roo.bootstrap.DocumentSlider
41051  * @extends Roo.bootstrap.Component
41052  * Bootstrap DocumentSlider class
41053  * 
41054  * @constructor
41055  * Create a new DocumentViewer
41056  * @param {Object} config The config object
41057  */
41058
41059 Roo.bootstrap.DocumentSlider = function(config){
41060     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41061     
41062     this.files = [];
41063     
41064     this.addEvents({
41065         /**
41066          * @event initial
41067          * Fire after initEvent
41068          * @param {Roo.bootstrap.DocumentSlider} this
41069          */
41070         "initial" : true,
41071         /**
41072          * @event update
41073          * Fire after update
41074          * @param {Roo.bootstrap.DocumentSlider} this
41075          */
41076         "update" : true,
41077         /**
41078          * @event click
41079          * Fire after click
41080          * @param {Roo.bootstrap.DocumentSlider} this
41081          */
41082         "click" : true
41083     });
41084 };
41085
41086 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41087     
41088     files : false,
41089     
41090     indicator : 0,
41091     
41092     getAutoCreate : function()
41093     {
41094         var cfg = {
41095             tag : 'div',
41096             cls : 'roo-document-slider',
41097             cn : [
41098                 {
41099                     tag : 'div',
41100                     cls : 'roo-document-slider-header',
41101                     cn : [
41102                         {
41103                             tag : 'div',
41104                             cls : 'roo-document-slider-header-title'
41105                         }
41106                     ]
41107                 },
41108                 {
41109                     tag : 'div',
41110                     cls : 'roo-document-slider-body',
41111                     cn : [
41112                         {
41113                             tag : 'div',
41114                             cls : 'roo-document-slider-prev',
41115                             cn : [
41116                                 {
41117                                     tag : 'i',
41118                                     cls : 'fa fa-chevron-left'
41119                                 }
41120                             ]
41121                         },
41122                         {
41123                             tag : 'div',
41124                             cls : 'roo-document-slider-thumb',
41125                             cn : [
41126                                 {
41127                                     tag : 'img',
41128                                     cls : 'roo-document-slider-image'
41129                                 }
41130                             ]
41131                         },
41132                         {
41133                             tag : 'div',
41134                             cls : 'roo-document-slider-next',
41135                             cn : [
41136                                 {
41137                                     tag : 'i',
41138                                     cls : 'fa fa-chevron-right'
41139                                 }
41140                             ]
41141                         }
41142                     ]
41143                 }
41144             ]
41145         };
41146         
41147         return cfg;
41148     },
41149     
41150     initEvents : function()
41151     {
41152         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41153         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41154         
41155         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41156         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41157         
41158         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41159         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41160         
41161         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41162         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41163         
41164         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41165         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41166         
41167         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41168         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41169         
41170         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41171         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41172         
41173         this.thumbEl.on('click', this.onClick, this);
41174         
41175         this.prevIndicator.on('click', this.prev, this);
41176         
41177         this.nextIndicator.on('click', this.next, this);
41178         
41179     },
41180     
41181     initial : function()
41182     {
41183         if(this.files.length){
41184             this.indicator = 1;
41185             this.update()
41186         }
41187         
41188         this.fireEvent('initial', this);
41189     },
41190     
41191     update : function()
41192     {
41193         this.imageEl.attr('src', this.files[this.indicator - 1]);
41194         
41195         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41196         
41197         this.prevIndicator.show();
41198         
41199         if(this.indicator == 1){
41200             this.prevIndicator.hide();
41201         }
41202         
41203         this.nextIndicator.show();
41204         
41205         if(this.indicator == this.files.length){
41206             this.nextIndicator.hide();
41207         }
41208         
41209         this.thumbEl.scrollTo('top');
41210         
41211         this.fireEvent('update', this);
41212     },
41213     
41214     onClick : function(e)
41215     {
41216         e.preventDefault();
41217         
41218         this.fireEvent('click', this);
41219     },
41220     
41221     prev : function(e)
41222     {
41223         e.preventDefault();
41224         
41225         this.indicator = Math.max(1, this.indicator - 1);
41226         
41227         this.update();
41228     },
41229     
41230     next : function(e)
41231     {
41232         e.preventDefault();
41233         
41234         this.indicator = Math.min(this.files.length, this.indicator + 1);
41235         
41236         this.update();
41237     }
41238 });
41239 /*
41240  * - LGPL
41241  *
41242  * RadioSet
41243  *
41244  *
41245  */
41246
41247 /**
41248  * @class Roo.bootstrap.form.RadioSet
41249  * @extends Roo.bootstrap.form.Input
41250  * @children Roo.bootstrap.form.Radio
41251  * Bootstrap RadioSet class
41252  * @cfg {String} indicatorpos (left|right) default left
41253  * @cfg {Boolean} inline (true|false) inline the element (default true)
41254  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41255  * @constructor
41256  * Create a new RadioSet
41257  * @param {Object} config The config object
41258  */
41259
41260 Roo.bootstrap.form.RadioSet = function(config){
41261     
41262     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41263     
41264     this.radioes = [];
41265     
41266     Roo.bootstrap.form.RadioSet.register(this);
41267     
41268     this.addEvents({
41269         /**
41270         * @event check
41271         * Fires when the element is checked or unchecked.
41272         * @param {Roo.bootstrap.form.RadioSet} this This radio
41273         * @param {Roo.bootstrap.form.Radio} item The checked item
41274         */
41275        check : true,
41276        /**
41277         * @event click
41278         * Fires when the element is click.
41279         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41280         * @param {Roo.bootstrap.form.Radio} item The checked item
41281         * @param {Roo.EventObject} e The event object
41282         */
41283        click : true
41284     });
41285     
41286 };
41287
41288 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41289
41290     radioes : false,
41291     
41292     inline : true,
41293     
41294     weight : '',
41295     
41296     indicatorpos : 'left',
41297     
41298     getAutoCreate : function()
41299     {
41300         var label = {
41301             tag : 'label',
41302             cls : 'roo-radio-set-label',
41303             cn : [
41304                 {
41305                     tag : 'span',
41306                     html : this.fieldLabel
41307                 }
41308             ]
41309         };
41310         if (Roo.bootstrap.version == 3) {
41311             
41312             
41313             if(this.indicatorpos == 'left'){
41314                 label.cn.unshift({
41315                     tag : 'i',
41316                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41317                     tooltip : 'This field is required'
41318                 });
41319             } else {
41320                 label.cn.push({
41321                     tag : 'i',
41322                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41323                     tooltip : 'This field is required'
41324                 });
41325             }
41326         }
41327         var items = {
41328             tag : 'div',
41329             cls : 'roo-radio-set-items'
41330         };
41331         
41332         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41333         
41334         if (align === 'left' && this.fieldLabel.length) {
41335             
41336             items = {
41337                 cls : "roo-radio-set-right", 
41338                 cn: [
41339                     items
41340                 ]
41341             };
41342             
41343             if(this.labelWidth > 12){
41344                 label.style = "width: " + this.labelWidth + 'px';
41345             }
41346             
41347             if(this.labelWidth < 13 && this.labelmd == 0){
41348                 this.labelmd = this.labelWidth;
41349             }
41350             
41351             if(this.labellg > 0){
41352                 label.cls += ' col-lg-' + this.labellg;
41353                 items.cls += ' col-lg-' + (12 - this.labellg);
41354             }
41355             
41356             if(this.labelmd > 0){
41357                 label.cls += ' col-md-' + this.labelmd;
41358                 items.cls += ' col-md-' + (12 - this.labelmd);
41359             }
41360             
41361             if(this.labelsm > 0){
41362                 label.cls += ' col-sm-' + this.labelsm;
41363                 items.cls += ' col-sm-' + (12 - this.labelsm);
41364             }
41365             
41366             if(this.labelxs > 0){
41367                 label.cls += ' col-xs-' + this.labelxs;
41368                 items.cls += ' col-xs-' + (12 - this.labelxs);
41369             }
41370         }
41371         
41372         var cfg = {
41373             tag : 'div',
41374             cls : 'roo-radio-set',
41375             cn : [
41376                 {
41377                     tag : 'input',
41378                     cls : 'roo-radio-set-input',
41379                     type : 'hidden',
41380                     name : this.name,
41381                     value : this.value ? this.value :  ''
41382                 },
41383                 label,
41384                 items
41385             ]
41386         };
41387         
41388         if(this.weight.length){
41389             cfg.cls += ' roo-radio-' + this.weight;
41390         }
41391         
41392         if(this.inline) {
41393             cfg.cls += ' roo-radio-set-inline';
41394         }
41395         
41396         var settings=this;
41397         ['xs','sm','md','lg'].map(function(size){
41398             if (settings[size]) {
41399                 cfg.cls += ' col-' + size + '-' + settings[size];
41400             }
41401         });
41402         
41403         return cfg;
41404         
41405     },
41406
41407     initEvents : function()
41408     {
41409         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41410         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41411         
41412         if(!this.fieldLabel.length){
41413             this.labelEl.hide();
41414         }
41415         
41416         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41417         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41418         
41419         this.indicator = this.indicatorEl();
41420         
41421         if(this.indicator){
41422             this.indicator.addClass('invisible');
41423         }
41424         
41425         this.originalValue = this.getValue();
41426         
41427     },
41428     
41429     inputEl: function ()
41430     {
41431         return this.el.select('.roo-radio-set-input', true).first();
41432     },
41433     
41434     getChildContainer : function()
41435     {
41436         return this.itemsEl;
41437     },
41438     
41439     register : function(item)
41440     {
41441         this.radioes.push(item);
41442         
41443     },
41444     
41445     validate : function()
41446     {   
41447         if(this.getVisibilityEl().hasClass('hidden')){
41448             return true;
41449         }
41450         
41451         var valid = false;
41452         
41453         Roo.each(this.radioes, function(i){
41454             if(!i.checked){
41455                 return;
41456             }
41457             
41458             valid = true;
41459             return false;
41460         });
41461         
41462         if(this.allowBlank) {
41463             return true;
41464         }
41465         
41466         if(this.disabled || valid){
41467             this.markValid();
41468             return true;
41469         }
41470         
41471         this.markInvalid();
41472         return false;
41473         
41474     },
41475     
41476     markValid : function()
41477     {
41478         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41479             this.indicatorEl().removeClass('visible');
41480             this.indicatorEl().addClass('invisible');
41481         }
41482         
41483         
41484         if (Roo.bootstrap.version == 3) {
41485             this.el.removeClass([this.invalidClass, this.validClass]);
41486             this.el.addClass(this.validClass);
41487         } else {
41488             this.el.removeClass(['is-invalid','is-valid']);
41489             this.el.addClass(['is-valid']);
41490         }
41491         this.fireEvent('valid', this);
41492     },
41493     
41494     markInvalid : function(msg)
41495     {
41496         if(this.allowBlank || this.disabled){
41497             return;
41498         }
41499         
41500         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41501             this.indicatorEl().removeClass('invisible');
41502             this.indicatorEl().addClass('visible');
41503         }
41504         if (Roo.bootstrap.version == 3) {
41505             this.el.removeClass([this.invalidClass, this.validClass]);
41506             this.el.addClass(this.invalidClass);
41507         } else {
41508             this.el.removeClass(['is-invalid','is-valid']);
41509             this.el.addClass(['is-invalid']);
41510         }
41511         
41512         this.fireEvent('invalid', this, msg);
41513         
41514     },
41515     
41516     setValue : function(v, suppressEvent)
41517     {   
41518         if(this.value === v){
41519             return;
41520         }
41521         
41522         this.value = v;
41523         
41524         if(this.rendered){
41525             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41526         }
41527         
41528         Roo.each(this.radioes, function(i){
41529             i.checked = false;
41530             i.el.removeClass('checked');
41531         });
41532         
41533         Roo.each(this.radioes, function(i){
41534             
41535             if(i.value === v || i.value.toString() === v.toString()){
41536                 i.checked = true;
41537                 i.el.addClass('checked');
41538                 
41539                 if(suppressEvent !== true){
41540                     this.fireEvent('check', this, i);
41541                 }
41542                 
41543                 return false;
41544             }
41545             
41546         }, this);
41547         
41548         this.validate();
41549     },
41550     
41551     clearInvalid : function(){
41552         
41553         if(!this.el || this.preventMark){
41554             return;
41555         }
41556         
41557         this.el.removeClass([this.invalidClass]);
41558         
41559         this.fireEvent('valid', this);
41560     }
41561     
41562 });
41563
41564 Roo.apply(Roo.bootstrap.form.RadioSet, {
41565     
41566     groups: {},
41567     
41568     register : function(set)
41569     {
41570         this.groups[set.name] = set;
41571     },
41572     
41573     get: function(name) 
41574     {
41575         if (typeof(this.groups[name]) == 'undefined') {
41576             return false;
41577         }
41578         
41579         return this.groups[name] ;
41580     }
41581     
41582 });
41583 /*
41584  * Based on:
41585  * Ext JS Library 1.1.1
41586  * Copyright(c) 2006-2007, Ext JS, LLC.
41587  *
41588  * Originally Released Under LGPL - original licence link has changed is not relivant.
41589  *
41590  * Fork - LGPL
41591  * <script type="text/javascript">
41592  */
41593
41594
41595 /**
41596  * @class Roo.bootstrap.SplitBar
41597  * @extends Roo.util.Observable
41598  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
41599  * <br><br>
41600  * Usage:
41601  * <pre><code>
41602 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
41603                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
41604 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
41605 split.minSize = 100;
41606 split.maxSize = 600;
41607 split.animate = true;
41608 split.on('moved', splitterMoved);
41609 </code></pre>
41610  * @constructor
41611  * Create a new SplitBar
41612  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
41613  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
41614  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41615  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
41616                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
41617                         position of the SplitBar).
41618  */
41619 Roo.bootstrap.SplitBar = function(cfg){
41620     
41621     /** @private */
41622     
41623     //{
41624     //  dragElement : elm
41625     //  resizingElement: el,
41626         // optional..
41627     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
41628     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
41629         // existingProxy ???
41630     //}
41631     
41632     this.el = Roo.get(cfg.dragElement, true);
41633     this.el.dom.unselectable = "on";
41634     /** @private */
41635     this.resizingEl = Roo.get(cfg.resizingElement, true);
41636
41637     /**
41638      * @private
41639      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41640      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
41641      * @type Number
41642      */
41643     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
41644     
41645     /**
41646      * The minimum size of the resizing element. (Defaults to 0)
41647      * @type Number
41648      */
41649     this.minSize = 0;
41650     
41651     /**
41652      * The maximum size of the resizing element. (Defaults to 2000)
41653      * @type Number
41654      */
41655     this.maxSize = 2000;
41656     
41657     /**
41658      * Whether to animate the transition to the new size
41659      * @type Boolean
41660      */
41661     this.animate = false;
41662     
41663     /**
41664      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
41665      * @type Boolean
41666      */
41667     this.useShim = false;
41668     
41669     /** @private */
41670     this.shim = null;
41671     
41672     if(!cfg.existingProxy){
41673         /** @private */
41674         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
41675     }else{
41676         this.proxy = Roo.get(cfg.existingProxy).dom;
41677     }
41678     /** @private */
41679     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
41680     
41681     /** @private */
41682     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
41683     
41684     /** @private */
41685     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
41686     
41687     /** @private */
41688     this.dragSpecs = {};
41689     
41690     /**
41691      * @private The adapter to use to positon and resize elements
41692      */
41693     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41694     this.adapter.init(this);
41695     
41696     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41697         /** @private */
41698         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
41699         this.el.addClass("roo-splitbar-h");
41700     }else{
41701         /** @private */
41702         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
41703         this.el.addClass("roo-splitbar-v");
41704     }
41705     
41706     this.addEvents({
41707         /**
41708          * @event resize
41709          * Fires when the splitter is moved (alias for {@link #event-moved})
41710          * @param {Roo.bootstrap.SplitBar} this
41711          * @param {Number} newSize the new width or height
41712          */
41713         "resize" : true,
41714         /**
41715          * @event moved
41716          * Fires when the splitter is moved
41717          * @param {Roo.bootstrap.SplitBar} this
41718          * @param {Number} newSize the new width or height
41719          */
41720         "moved" : true,
41721         /**
41722          * @event beforeresize
41723          * Fires before the splitter is dragged
41724          * @param {Roo.bootstrap.SplitBar} this
41725          */
41726         "beforeresize" : true,
41727
41728         "beforeapply" : true
41729     });
41730
41731     Roo.util.Observable.call(this);
41732 };
41733
41734 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
41735     onStartProxyDrag : function(x, y){
41736         this.fireEvent("beforeresize", this);
41737         if(!this.overlay){
41738             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
41739             o.unselectable();
41740             o.enableDisplayMode("block");
41741             // all splitbars share the same overlay
41742             Roo.bootstrap.SplitBar.prototype.overlay = o;
41743         }
41744         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
41745         this.overlay.show();
41746         Roo.get(this.proxy).setDisplayed("block");
41747         var size = this.adapter.getElementSize(this);
41748         this.activeMinSize = this.getMinimumSize();;
41749         this.activeMaxSize = this.getMaximumSize();;
41750         var c1 = size - this.activeMinSize;
41751         var c2 = Math.max(this.activeMaxSize - size, 0);
41752         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41753             this.dd.resetConstraints();
41754             this.dd.setXConstraint(
41755                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
41756                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
41757             );
41758             this.dd.setYConstraint(0, 0);
41759         }else{
41760             this.dd.resetConstraints();
41761             this.dd.setXConstraint(0, 0);
41762             this.dd.setYConstraint(
41763                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
41764                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
41765             );
41766          }
41767         this.dragSpecs.startSize = size;
41768         this.dragSpecs.startPoint = [x, y];
41769         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
41770     },
41771     
41772     /** 
41773      * @private Called after the drag operation by the DDProxy
41774      */
41775     onEndProxyDrag : function(e){
41776         Roo.get(this.proxy).setDisplayed(false);
41777         var endPoint = Roo.lib.Event.getXY(e);
41778         if(this.overlay){
41779             this.overlay.hide();
41780         }
41781         var newSize;
41782         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41783             newSize = this.dragSpecs.startSize + 
41784                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
41785                     endPoint[0] - this.dragSpecs.startPoint[0] :
41786                     this.dragSpecs.startPoint[0] - endPoint[0]
41787                 );
41788         }else{
41789             newSize = this.dragSpecs.startSize + 
41790                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
41791                     endPoint[1] - this.dragSpecs.startPoint[1] :
41792                     this.dragSpecs.startPoint[1] - endPoint[1]
41793                 );
41794         }
41795         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
41796         if(newSize != this.dragSpecs.startSize){
41797             if(this.fireEvent('beforeapply', this, newSize) !== false){
41798                 this.adapter.setElementSize(this, newSize);
41799                 this.fireEvent("moved", this, newSize);
41800                 this.fireEvent("resize", this, newSize);
41801             }
41802         }
41803     },
41804     
41805     /**
41806      * Get the adapter this SplitBar uses
41807      * @return The adapter object
41808      */
41809     getAdapter : function(){
41810         return this.adapter;
41811     },
41812     
41813     /**
41814      * Set the adapter this SplitBar uses
41815      * @param {Object} adapter A SplitBar adapter object
41816      */
41817     setAdapter : function(adapter){
41818         this.adapter = adapter;
41819         this.adapter.init(this);
41820     },
41821     
41822     /**
41823      * Gets the minimum size for the resizing element
41824      * @return {Number} The minimum size
41825      */
41826     getMinimumSize : function(){
41827         return this.minSize;
41828     },
41829     
41830     /**
41831      * Sets the minimum size for the resizing element
41832      * @param {Number} minSize The minimum size
41833      */
41834     setMinimumSize : function(minSize){
41835         this.minSize = minSize;
41836     },
41837     
41838     /**
41839      * Gets the maximum size for the resizing element
41840      * @return {Number} The maximum size
41841      */
41842     getMaximumSize : function(){
41843         return this.maxSize;
41844     },
41845     
41846     /**
41847      * Sets the maximum size for the resizing element
41848      * @param {Number} maxSize The maximum size
41849      */
41850     setMaximumSize : function(maxSize){
41851         this.maxSize = maxSize;
41852     },
41853     
41854     /**
41855      * Sets the initialize size for the resizing element
41856      * @param {Number} size The initial size
41857      */
41858     setCurrentSize : function(size){
41859         var oldAnimate = this.animate;
41860         this.animate = false;
41861         this.adapter.setElementSize(this, size);
41862         this.animate = oldAnimate;
41863     },
41864     
41865     /**
41866      * Destroy this splitbar. 
41867      * @param {Boolean} removeEl True to remove the element
41868      */
41869     destroy : function(removeEl){
41870         if(this.shim){
41871             this.shim.remove();
41872         }
41873         this.dd.unreg();
41874         this.proxy.parentNode.removeChild(this.proxy);
41875         if(removeEl){
41876             this.el.remove();
41877         }
41878     }
41879 });
41880
41881 /**
41882  * @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.
41883  */
41884 Roo.bootstrap.SplitBar.createProxy = function(dir){
41885     var proxy = new Roo.Element(document.createElement("div"));
41886     proxy.unselectable();
41887     var cls = 'roo-splitbar-proxy';
41888     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
41889     document.body.appendChild(proxy.dom);
41890     return proxy.dom;
41891 };
41892
41893 /** 
41894  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
41895  * Default Adapter. It assumes the splitter and resizing element are not positioned
41896  * elements and only gets/sets the width of the element. Generally used for table based layouts.
41897  */
41898 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
41899 };
41900
41901 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
41902     // do nothing for now
41903     init : function(s){
41904     
41905     },
41906     /**
41907      * Called before drag operations to get the current size of the resizing element. 
41908      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41909      */
41910      getElementSize : function(s){
41911         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41912             return s.resizingEl.getWidth();
41913         }else{
41914             return s.resizingEl.getHeight();
41915         }
41916     },
41917     
41918     /**
41919      * Called after drag operations to set the size of the resizing element.
41920      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41921      * @param {Number} newSize The new size to set
41922      * @param {Function} onComplete A function to be invoked when resizing is complete
41923      */
41924     setElementSize : function(s, newSize, onComplete){
41925         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41926             if(!s.animate){
41927                 s.resizingEl.setWidth(newSize);
41928                 if(onComplete){
41929                     onComplete(s, newSize);
41930                 }
41931             }else{
41932                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
41933             }
41934         }else{
41935             
41936             if(!s.animate){
41937                 s.resizingEl.setHeight(newSize);
41938                 if(onComplete){
41939                     onComplete(s, newSize);
41940                 }
41941             }else{
41942                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
41943             }
41944         }
41945     }
41946 };
41947
41948 /** 
41949  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
41950  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
41951  * Adapter that  moves the splitter element to align with the resized sizing element. 
41952  * Used with an absolute positioned SplitBar.
41953  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
41954  * document.body, make sure you assign an id to the body element.
41955  */
41956 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
41957     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41958     this.container = Roo.get(container);
41959 };
41960
41961 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
41962     init : function(s){
41963         this.basic.init(s);
41964     },
41965     
41966     getElementSize : function(s){
41967         return this.basic.getElementSize(s);
41968     },
41969     
41970     setElementSize : function(s, newSize, onComplete){
41971         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
41972     },
41973     
41974     moveSplitter : function(s){
41975         var yes = Roo.bootstrap.SplitBar;
41976         switch(s.placement){
41977             case yes.LEFT:
41978                 s.el.setX(s.resizingEl.getRight());
41979                 break;
41980             case yes.RIGHT:
41981                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
41982                 break;
41983             case yes.TOP:
41984                 s.el.setY(s.resizingEl.getBottom());
41985                 break;
41986             case yes.BOTTOM:
41987                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
41988                 break;
41989         }
41990     }
41991 };
41992
41993 /**
41994  * Orientation constant - Create a vertical SplitBar
41995  * @static
41996  * @type Number
41997  */
41998 Roo.bootstrap.SplitBar.VERTICAL = 1;
41999
42000 /**
42001  * Orientation constant - Create a horizontal SplitBar
42002  * @static
42003  * @type Number
42004  */
42005 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42006
42007 /**
42008  * Placement constant - The resizing element is to the left of the splitter element
42009  * @static
42010  * @type Number
42011  */
42012 Roo.bootstrap.SplitBar.LEFT = 1;
42013
42014 /**
42015  * Placement constant - The resizing element is to the right of the splitter element
42016  * @static
42017  * @type Number
42018  */
42019 Roo.bootstrap.SplitBar.RIGHT = 2;
42020
42021 /**
42022  * Placement constant - The resizing element is positioned above the splitter element
42023  * @static
42024  * @type Number
42025  */
42026 Roo.bootstrap.SplitBar.TOP = 3;
42027
42028 /**
42029  * Placement constant - The resizing element is positioned under splitter element
42030  * @static
42031  * @type Number
42032  */
42033 Roo.bootstrap.SplitBar.BOTTOM = 4;
42034 /*
42035  * Based on:
42036  * Ext JS Library 1.1.1
42037  * Copyright(c) 2006-2007, Ext JS, LLC.
42038  *
42039  * Originally Released Under LGPL - original licence link has changed is not relivant.
42040  *
42041  * Fork - LGPL
42042  * <script type="text/javascript">
42043  */
42044
42045 /**
42046  * @class Roo.bootstrap.layout.Manager
42047  * @extends Roo.bootstrap.Component
42048  * @abstract
42049  * Base class for layout managers.
42050  */
42051 Roo.bootstrap.layout.Manager = function(config)
42052 {
42053     this.monitorWindowResize = true; // do this before we apply configuration.
42054     
42055     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42056
42057
42058
42059
42060
42061     /** false to disable window resize monitoring @type Boolean */
42062     
42063     this.regions = {};
42064     this.addEvents({
42065         /**
42066          * @event layout
42067          * Fires when a layout is performed.
42068          * @param {Roo.LayoutManager} this
42069          */
42070         "layout" : true,
42071         /**
42072          * @event regionresized
42073          * Fires when the user resizes a region.
42074          * @param {Roo.LayoutRegion} region The resized region
42075          * @param {Number} newSize The new size (width for east/west, height for north/south)
42076          */
42077         "regionresized" : true,
42078         /**
42079          * @event regioncollapsed
42080          * Fires when a region is collapsed.
42081          * @param {Roo.LayoutRegion} region The collapsed region
42082          */
42083         "regioncollapsed" : true,
42084         /**
42085          * @event regionexpanded
42086          * Fires when a region is expanded.
42087          * @param {Roo.LayoutRegion} region The expanded region
42088          */
42089         "regionexpanded" : true
42090     });
42091     this.updating = false;
42092
42093     if (config.el) {
42094         this.el = Roo.get(config.el);
42095         this.initEvents();
42096     }
42097
42098 };
42099
42100 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42101
42102
42103     regions : null,
42104
42105     monitorWindowResize : true,
42106
42107
42108     updating : false,
42109
42110
42111     onRender : function(ct, position)
42112     {
42113         if(!this.el){
42114             this.el = Roo.get(ct);
42115             this.initEvents();
42116         }
42117         //this.fireEvent('render',this);
42118     },
42119
42120
42121     initEvents: function()
42122     {
42123
42124
42125         // ie scrollbar fix
42126         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42127             document.body.scroll = "no";
42128         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42129             this.el.position('relative');
42130         }
42131         this.id = this.el.id;
42132         this.el.addClass("roo-layout-container");
42133         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42134         if(this.el.dom != document.body ) {
42135             this.el.on('resize', this.layout,this);
42136             this.el.on('show', this.layout,this);
42137         }
42138
42139     },
42140
42141     /**
42142      * Returns true if this layout is currently being updated
42143      * @return {Boolean}
42144      */
42145     isUpdating : function(){
42146         return this.updating;
42147     },
42148
42149     /**
42150      * Suspend the LayoutManager from doing auto-layouts while
42151      * making multiple add or remove calls
42152      */
42153     beginUpdate : function(){
42154         this.updating = true;
42155     },
42156
42157     /**
42158      * Restore auto-layouts and optionally disable the manager from performing a layout
42159      * @param {Boolean} noLayout true to disable a layout update
42160      */
42161     endUpdate : function(noLayout){
42162         this.updating = false;
42163         if(!noLayout){
42164             this.layout();
42165         }
42166     },
42167
42168     layout: function(){
42169         // abstract...
42170     },
42171
42172     onRegionResized : function(region, newSize){
42173         this.fireEvent("regionresized", region, newSize);
42174         this.layout();
42175     },
42176
42177     onRegionCollapsed : function(region){
42178         this.fireEvent("regioncollapsed", region);
42179     },
42180
42181     onRegionExpanded : function(region){
42182         this.fireEvent("regionexpanded", region);
42183     },
42184
42185     /**
42186      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42187      * performs box-model adjustments.
42188      * @return {Object} The size as an object {width: (the width), height: (the height)}
42189      */
42190     getViewSize : function()
42191     {
42192         var size;
42193         if(this.el.dom != document.body){
42194             size = this.el.getSize();
42195         }else{
42196             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42197         }
42198         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42199         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42200         return size;
42201     },
42202
42203     /**
42204      * Returns the Element this layout is bound to.
42205      * @return {Roo.Element}
42206      */
42207     getEl : function(){
42208         return this.el;
42209     },
42210
42211     /**
42212      * Returns the specified region.
42213      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42214      * @return {Roo.LayoutRegion}
42215      */
42216     getRegion : function(target){
42217         return this.regions[target.toLowerCase()];
42218     },
42219
42220     onWindowResize : function(){
42221         if(this.monitorWindowResize){
42222             this.layout();
42223         }
42224     }
42225 });
42226 /*
42227  * Based on:
42228  * Ext JS Library 1.1.1
42229  * Copyright(c) 2006-2007, Ext JS, LLC.
42230  *
42231  * Originally Released Under LGPL - original licence link has changed is not relivant.
42232  *
42233  * Fork - LGPL
42234  * <script type="text/javascript">
42235  */
42236 /**
42237  * @class Roo.bootstrap.layout.Border
42238  * @extends Roo.bootstrap.layout.Manager
42239  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42240  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42241  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42242  * please see: examples/bootstrap/nested.html<br><br>
42243  
42244 <b>The container the layout is rendered into can be either the body element or any other element.
42245 If it is not the body element, the container needs to either be an absolute positioned element,
42246 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42247 the container size if it is not the body element.</b>
42248
42249 * @constructor
42250 * Create a new Border
42251 * @param {Object} config Configuration options
42252  */
42253 Roo.bootstrap.layout.Border = function(config){
42254     config = config || {};
42255     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42256     
42257     
42258     
42259     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42260         if(config[region]){
42261             config[region].region = region;
42262             this.addRegion(config[region]);
42263         }
42264     },this);
42265     
42266 };
42267
42268 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42269
42270 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42271     
42272         /**
42273          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42274          */
42275         /**
42276          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42277          */
42278         /**
42279          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42280          */
42281         /**
42282          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42283          */
42284         /**
42285          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42286          */
42287         
42288         
42289         
42290         
42291     parent : false, // this might point to a 'nest' or a ???
42292     
42293     /**
42294      * Creates and adds a new region if it doesn't already exist.
42295      * @param {String} target The target region key (north, south, east, west or center).
42296      * @param {Object} config The regions config object
42297      * @return {BorderLayoutRegion} The new region
42298      */
42299     addRegion : function(config)
42300     {
42301         if(!this.regions[config.region]){
42302             var r = this.factory(config);
42303             this.bindRegion(r);
42304         }
42305         return this.regions[config.region];
42306     },
42307
42308     // private (kinda)
42309     bindRegion : function(r){
42310         this.regions[r.config.region] = r;
42311         
42312         r.on("visibilitychange",    this.layout, this);
42313         r.on("paneladded",          this.layout, this);
42314         r.on("panelremoved",        this.layout, this);
42315         r.on("invalidated",         this.layout, this);
42316         r.on("resized",             this.onRegionResized, this);
42317         r.on("collapsed",           this.onRegionCollapsed, this);
42318         r.on("expanded",            this.onRegionExpanded, this);
42319     },
42320
42321     /**
42322      * Performs a layout update.
42323      */
42324     layout : function()
42325     {
42326         if(this.updating) {
42327             return;
42328         }
42329         
42330         // render all the rebions if they have not been done alreayd?
42331         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42332             if(this.regions[region] && !this.regions[region].bodyEl){
42333                 this.regions[region].onRender(this.el)
42334             }
42335         },this);
42336         
42337         var size = this.getViewSize();
42338         var w = size.width;
42339         var h = size.height;
42340         var centerW = w;
42341         var centerH = h;
42342         var centerY = 0;
42343         var centerX = 0;
42344         //var x = 0, y = 0;
42345
42346         var rs = this.regions;
42347         var north = rs["north"];
42348         var south = rs["south"]; 
42349         var west = rs["west"];
42350         var east = rs["east"];
42351         var center = rs["center"];
42352         //if(this.hideOnLayout){ // not supported anymore
42353             //c.el.setStyle("display", "none");
42354         //}
42355         if(north && north.isVisible()){
42356             var b = north.getBox();
42357             var m = north.getMargins();
42358             b.width = w - (m.left+m.right);
42359             b.x = m.left;
42360             b.y = m.top;
42361             centerY = b.height + b.y + m.bottom;
42362             centerH -= centerY;
42363             north.updateBox(this.safeBox(b));
42364         }
42365         if(south && south.isVisible()){
42366             var b = south.getBox();
42367             var m = south.getMargins();
42368             b.width = w - (m.left+m.right);
42369             b.x = m.left;
42370             var totalHeight = (b.height + m.top + m.bottom);
42371             b.y = h - totalHeight + m.top;
42372             centerH -= totalHeight;
42373             south.updateBox(this.safeBox(b));
42374         }
42375         if(west && west.isVisible()){
42376             var b = west.getBox();
42377             var m = west.getMargins();
42378             b.height = centerH - (m.top+m.bottom);
42379             b.x = m.left;
42380             b.y = centerY + m.top;
42381             var totalWidth = (b.width + m.left + m.right);
42382             centerX += totalWidth;
42383             centerW -= totalWidth;
42384             west.updateBox(this.safeBox(b));
42385         }
42386         if(east && east.isVisible()){
42387             var b = east.getBox();
42388             var m = east.getMargins();
42389             b.height = centerH - (m.top+m.bottom);
42390             var totalWidth = (b.width + m.left + m.right);
42391             b.x = w - totalWidth + m.left;
42392             b.y = centerY + m.top;
42393             centerW -= totalWidth;
42394             east.updateBox(this.safeBox(b));
42395         }
42396         if(center){
42397             var m = center.getMargins();
42398             var centerBox = {
42399                 x: centerX + m.left,
42400                 y: centerY + m.top,
42401                 width: centerW - (m.left+m.right),
42402                 height: centerH - (m.top+m.bottom)
42403             };
42404             //if(this.hideOnLayout){
42405                 //center.el.setStyle("display", "block");
42406             //}
42407             center.updateBox(this.safeBox(centerBox));
42408         }
42409         this.el.repaint();
42410         this.fireEvent("layout", this);
42411     },
42412
42413     // private
42414     safeBox : function(box){
42415         box.width = Math.max(0, box.width);
42416         box.height = Math.max(0, box.height);
42417         return box;
42418     },
42419
42420     /**
42421      * Adds a ContentPanel (or subclass) to this layout.
42422      * @param {String} target The target region key (north, south, east, west or center).
42423      * @param {Roo.ContentPanel} panel The panel to add
42424      * @return {Roo.ContentPanel} The added panel
42425      */
42426     add : function(target, panel){
42427          
42428         target = target.toLowerCase();
42429         return this.regions[target].add(panel);
42430     },
42431
42432     /**
42433      * Remove a ContentPanel (or subclass) to this layout.
42434      * @param {String} target The target region key (north, south, east, west or center).
42435      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42436      * @return {Roo.ContentPanel} The removed panel
42437      */
42438     remove : function(target, panel){
42439         target = target.toLowerCase();
42440         return this.regions[target].remove(panel);
42441     },
42442
42443     /**
42444      * Searches all regions for a panel with the specified id
42445      * @param {String} panelId
42446      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42447      */
42448     findPanel : function(panelId){
42449         var rs = this.regions;
42450         for(var target in rs){
42451             if(typeof rs[target] != "function"){
42452                 var p = rs[target].getPanel(panelId);
42453                 if(p){
42454                     return p;
42455                 }
42456             }
42457         }
42458         return null;
42459     },
42460
42461     /**
42462      * Searches all regions for a panel with the specified id and activates (shows) it.
42463      * @param {String/ContentPanel} panelId The panels id or the panel itself
42464      * @return {Roo.ContentPanel} The shown panel or null
42465      */
42466     showPanel : function(panelId) {
42467       var rs = this.regions;
42468       for(var target in rs){
42469          var r = rs[target];
42470          if(typeof r != "function"){
42471             if(r.hasPanel(panelId)){
42472                return r.showPanel(panelId);
42473             }
42474          }
42475       }
42476       return null;
42477    },
42478
42479    /**
42480      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42481      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42482      */
42483    /*
42484     restoreState : function(provider){
42485         if(!provider){
42486             provider = Roo.state.Manager;
42487         }
42488         var sm = new Roo.LayoutStateManager();
42489         sm.init(this, provider);
42490     },
42491 */
42492  
42493  
42494     /**
42495      * Adds a xtype elements to the layout.
42496      * <pre><code>
42497
42498 layout.addxtype({
42499        xtype : 'ContentPanel',
42500        region: 'west',
42501        items: [ .... ]
42502    }
42503 );
42504
42505 layout.addxtype({
42506         xtype : 'NestedLayoutPanel',
42507         region: 'west',
42508         layout: {
42509            center: { },
42510            west: { }   
42511         },
42512         items : [ ... list of content panels or nested layout panels.. ]
42513    }
42514 );
42515 </code></pre>
42516      * @param {Object} cfg Xtype definition of item to add.
42517      */
42518     addxtype : function(cfg)
42519     {
42520         // basically accepts a pannel...
42521         // can accept a layout region..!?!?
42522         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42523         
42524         
42525         // theory?  children can only be panels??
42526         
42527         //if (!cfg.xtype.match(/Panel$/)) {
42528         //    return false;
42529         //}
42530         var ret = false;
42531         
42532         if (typeof(cfg.region) == 'undefined') {
42533             Roo.log("Failed to add Panel, region was not set");
42534             Roo.log(cfg);
42535             return false;
42536         }
42537         var region = cfg.region;
42538         delete cfg.region;
42539         
42540           
42541         var xitems = [];
42542         if (cfg.items) {
42543             xitems = cfg.items;
42544             delete cfg.items;
42545         }
42546         var nb = false;
42547         
42548         if ( region == 'center') {
42549             Roo.log("Center: " + cfg.title);
42550         }
42551         
42552         
42553         switch(cfg.xtype) 
42554         {
42555             case 'Content':  // ContentPanel (el, cfg)
42556             case 'Scroll':  // ContentPanel (el, cfg)
42557             case 'View': 
42558                 cfg.autoCreate = cfg.autoCreate || true;
42559                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42560                 //} else {
42561                 //    var el = this.el.createChild();
42562                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42563                 //}
42564                 
42565                 this.add(region, ret);
42566                 break;
42567             
42568             /*
42569             case 'TreePanel': // our new panel!
42570                 cfg.el = this.el.createChild();
42571                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42572                 this.add(region, ret);
42573                 break;
42574             */
42575             
42576             case 'Nest': 
42577                 // create a new Layout (which is  a Border Layout...
42578                 
42579                 var clayout = cfg.layout;
42580                 clayout.el  = this.el.createChild();
42581                 clayout.items   = clayout.items  || [];
42582                 
42583                 delete cfg.layout;
42584                 
42585                 // replace this exitems with the clayout ones..
42586                 xitems = clayout.items;
42587                  
42588                 // force background off if it's in center...
42589                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42590                     cfg.background = false;
42591                 }
42592                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
42593                 
42594                 
42595                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42596                 //console.log('adding nested layout panel '  + cfg.toSource());
42597                 this.add(region, ret);
42598                 nb = {}; /// find first...
42599                 break;
42600             
42601             case 'Grid':
42602                 
42603                 // needs grid and region
42604                 
42605                 //var el = this.getRegion(region).el.createChild();
42606                 /*
42607                  *var el = this.el.createChild();
42608                 // create the grid first...
42609                 cfg.grid.container = el;
42610                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
42611                 */
42612                 
42613                 if (region == 'center' && this.active ) {
42614                     cfg.background = false;
42615                 }
42616                 
42617                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42618                 
42619                 this.add(region, ret);
42620                 /*
42621                 if (cfg.background) {
42622                     // render grid on panel activation (if panel background)
42623                     ret.on('activate', function(gp) {
42624                         if (!gp.grid.rendered) {
42625                     //        gp.grid.render(el);
42626                         }
42627                     });
42628                 } else {
42629                   //  cfg.grid.render(el);
42630                 }
42631                 */
42632                 break;
42633            
42634            
42635             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
42636                 // it was the old xcomponent building that caused this before.
42637                 // espeically if border is the top element in the tree.
42638                 ret = this;
42639                 break; 
42640                 
42641                     
42642                 
42643                 
42644                 
42645             default:
42646                 /*
42647                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
42648                     
42649                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42650                     this.add(region, ret);
42651                 } else {
42652                 */
42653                     Roo.log(cfg);
42654                     throw "Can not add '" + cfg.xtype + "' to Border";
42655                     return null;
42656              
42657                                 
42658              
42659         }
42660         this.beginUpdate();
42661         // add children..
42662         var region = '';
42663         var abn = {};
42664         Roo.each(xitems, function(i)  {
42665             region = nb && i.region ? i.region : false;
42666             
42667             var add = ret.addxtype(i);
42668            
42669             if (region) {
42670                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
42671                 if (!i.background) {
42672                     abn[region] = nb[region] ;
42673                 }
42674             }
42675             
42676         });
42677         this.endUpdate();
42678
42679         // make the last non-background panel active..
42680         //if (nb) { Roo.log(abn); }
42681         if (nb) {
42682             
42683             for(var r in abn) {
42684                 region = this.getRegion(r);
42685                 if (region) {
42686                     // tried using nb[r], but it does not work..
42687                      
42688                     region.showPanel(abn[r]);
42689                    
42690                 }
42691             }
42692         }
42693         return ret;
42694         
42695     },
42696     
42697     
42698 // private
42699     factory : function(cfg)
42700     {
42701         
42702         var validRegions = Roo.bootstrap.layout.Border.regions;
42703
42704         var target = cfg.region;
42705         cfg.mgr = this;
42706         
42707         var r = Roo.bootstrap.layout;
42708         Roo.log(target);
42709         switch(target){
42710             case "north":
42711                 return new r.North(cfg);
42712             case "south":
42713                 return new r.South(cfg);
42714             case "east":
42715                 return new r.East(cfg);
42716             case "west":
42717                 return new r.West(cfg);
42718             case "center":
42719                 return new r.Center(cfg);
42720         }
42721         throw 'Layout region "'+target+'" not supported.';
42722     }
42723     
42724     
42725 });
42726  /*
42727  * Based on:
42728  * Ext JS Library 1.1.1
42729  * Copyright(c) 2006-2007, Ext JS, LLC.
42730  *
42731  * Originally Released Under LGPL - original licence link has changed is not relivant.
42732  *
42733  * Fork - LGPL
42734  * <script type="text/javascript">
42735  */
42736  
42737 /**
42738  * @class Roo.bootstrap.layout.Basic
42739  * @extends Roo.util.Observable
42740  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42741  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42742  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42743  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
42744  * @cfg {string}   region  the region that it inhabits..
42745  * @cfg {bool}   skipConfig skip config?
42746  * 
42747
42748  */
42749 Roo.bootstrap.layout.Basic = function(config){
42750     
42751     this.mgr = config.mgr;
42752     
42753     this.position = config.region;
42754     
42755     var skipConfig = config.skipConfig;
42756     
42757     this.events = {
42758         /**
42759          * @scope Roo.BasicLayoutRegion
42760          */
42761         
42762         /**
42763          * @event beforeremove
42764          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42765          * @param {Roo.LayoutRegion} this
42766          * @param {Roo.ContentPanel} panel The panel
42767          * @param {Object} e The cancel event object
42768          */
42769         "beforeremove" : true,
42770         /**
42771          * @event invalidated
42772          * Fires when the layout for this region is changed.
42773          * @param {Roo.LayoutRegion} this
42774          */
42775         "invalidated" : true,
42776         /**
42777          * @event visibilitychange
42778          * Fires when this region is shown or hidden 
42779          * @param {Roo.LayoutRegion} this
42780          * @param {Boolean} visibility true or false
42781          */
42782         "visibilitychange" : true,
42783         /**
42784          * @event paneladded
42785          * Fires when a panel is added. 
42786          * @param {Roo.LayoutRegion} this
42787          * @param {Roo.ContentPanel} panel The panel
42788          */
42789         "paneladded" : true,
42790         /**
42791          * @event panelremoved
42792          * Fires when a panel is removed. 
42793          * @param {Roo.LayoutRegion} this
42794          * @param {Roo.ContentPanel} panel The panel
42795          */
42796         "panelremoved" : true,
42797         /**
42798          * @event beforecollapse
42799          * Fires when this region before collapse.
42800          * @param {Roo.LayoutRegion} this
42801          */
42802         "beforecollapse" : true,
42803         /**
42804          * @event collapsed
42805          * Fires when this region is collapsed.
42806          * @param {Roo.LayoutRegion} this
42807          */
42808         "collapsed" : true,
42809         /**
42810          * @event expanded
42811          * Fires when this region is expanded.
42812          * @param {Roo.LayoutRegion} this
42813          */
42814         "expanded" : true,
42815         /**
42816          * @event slideshow
42817          * Fires when this region is slid into view.
42818          * @param {Roo.LayoutRegion} this
42819          */
42820         "slideshow" : true,
42821         /**
42822          * @event slidehide
42823          * Fires when this region slides out of view. 
42824          * @param {Roo.LayoutRegion} this
42825          */
42826         "slidehide" : true,
42827         /**
42828          * @event panelactivated
42829          * Fires when a panel is activated. 
42830          * @param {Roo.LayoutRegion} this
42831          * @param {Roo.ContentPanel} panel The activated panel
42832          */
42833         "panelactivated" : true,
42834         /**
42835          * @event resized
42836          * Fires when the user resizes this region. 
42837          * @param {Roo.LayoutRegion} this
42838          * @param {Number} newSize The new size (width for east/west, height for north/south)
42839          */
42840         "resized" : true
42841     };
42842     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42843     this.panels = new Roo.util.MixedCollection();
42844     this.panels.getKey = this.getPanelId.createDelegate(this);
42845     this.box = null;
42846     this.activePanel = null;
42847     // ensure listeners are added...
42848     
42849     if (config.listeners || config.events) {
42850         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
42851             listeners : config.listeners || {},
42852             events : config.events || {}
42853         });
42854     }
42855     
42856     if(skipConfig !== true){
42857         this.applyConfig(config);
42858     }
42859 };
42860
42861 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
42862 {
42863     getPanelId : function(p){
42864         return p.getId();
42865     },
42866     
42867     applyConfig : function(config){
42868         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42869         this.config = config;
42870         
42871     },
42872     
42873     /**
42874      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42875      * the width, for horizontal (north, south) the height.
42876      * @param {Number} newSize The new width or height
42877      */
42878     resizeTo : function(newSize){
42879         var el = this.el ? this.el :
42880                  (this.activePanel ? this.activePanel.getEl() : null);
42881         if(el){
42882             switch(this.position){
42883                 case "east":
42884                 case "west":
42885                     el.setWidth(newSize);
42886                     this.fireEvent("resized", this, newSize);
42887                 break;
42888                 case "north":
42889                 case "south":
42890                     el.setHeight(newSize);
42891                     this.fireEvent("resized", this, newSize);
42892                 break;                
42893             }
42894         }
42895     },
42896     
42897     getBox : function(){
42898         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42899     },
42900     
42901     getMargins : function(){
42902         return this.margins;
42903     },
42904     
42905     updateBox : function(box){
42906         this.box = box;
42907         var el = this.activePanel.getEl();
42908         el.dom.style.left = box.x + "px";
42909         el.dom.style.top = box.y + "px";
42910         this.activePanel.setSize(box.width, box.height);
42911     },
42912     
42913     /**
42914      * Returns the container element for this region.
42915      * @return {Roo.Element}
42916      */
42917     getEl : function(){
42918         return this.activePanel;
42919     },
42920     
42921     /**
42922      * Returns true if this region is currently visible.
42923      * @return {Boolean}
42924      */
42925     isVisible : function(){
42926         return this.activePanel ? true : false;
42927     },
42928     
42929     setActivePanel : function(panel){
42930         panel = this.getPanel(panel);
42931         if(this.activePanel && this.activePanel != panel){
42932             this.activePanel.setActiveState(false);
42933             this.activePanel.getEl().setLeftTop(-10000,-10000);
42934         }
42935         this.activePanel = panel;
42936         panel.setActiveState(true);
42937         if(this.box){
42938             panel.setSize(this.box.width, this.box.height);
42939         }
42940         this.fireEvent("panelactivated", this, panel);
42941         this.fireEvent("invalidated");
42942     },
42943     
42944     /**
42945      * Show the specified panel.
42946      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42947      * @return {Roo.ContentPanel} The shown panel or null
42948      */
42949     showPanel : function(panel){
42950         panel = this.getPanel(panel);
42951         if(panel){
42952             this.setActivePanel(panel);
42953         }
42954         return panel;
42955     },
42956     
42957     /**
42958      * Get the active panel for this region.
42959      * @return {Roo.ContentPanel} The active panel or null
42960      */
42961     getActivePanel : function(){
42962         return this.activePanel;
42963     },
42964     
42965     /**
42966      * Add the passed ContentPanel(s)
42967      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42968      * @return {Roo.ContentPanel} The panel added (if only one was added)
42969      */
42970     add : function(panel){
42971         if(arguments.length > 1){
42972             for(var i = 0, len = arguments.length; i < len; i++) {
42973                 this.add(arguments[i]);
42974             }
42975             return null;
42976         }
42977         if(this.hasPanel(panel)){
42978             this.showPanel(panel);
42979             return panel;
42980         }
42981         var el = panel.getEl();
42982         if(el.dom.parentNode != this.mgr.el.dom){
42983             this.mgr.el.dom.appendChild(el.dom);
42984         }
42985         if(panel.setRegion){
42986             panel.setRegion(this);
42987         }
42988         this.panels.add(panel);
42989         el.setStyle("position", "absolute");
42990         if(!panel.background){
42991             this.setActivePanel(panel);
42992             if(this.config.initialSize && this.panels.getCount()==1){
42993                 this.resizeTo(this.config.initialSize);
42994             }
42995         }
42996         this.fireEvent("paneladded", this, panel);
42997         return panel;
42998     },
42999     
43000     /**
43001      * Returns true if the panel is in this region.
43002      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43003      * @return {Boolean}
43004      */
43005     hasPanel : function(panel){
43006         if(typeof panel == "object"){ // must be panel obj
43007             panel = panel.getId();
43008         }
43009         return this.getPanel(panel) ? true : false;
43010     },
43011     
43012     /**
43013      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43014      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43015      * @param {Boolean} preservePanel Overrides the config preservePanel option
43016      * @return {Roo.ContentPanel} The panel that was removed
43017      */
43018     remove : function(panel, preservePanel){
43019         panel = this.getPanel(panel);
43020         if(!panel){
43021             return null;
43022         }
43023         var e = {};
43024         this.fireEvent("beforeremove", this, panel, e);
43025         if(e.cancel === true){
43026             return null;
43027         }
43028         var panelId = panel.getId();
43029         this.panels.removeKey(panelId);
43030         return panel;
43031     },
43032     
43033     /**
43034      * Returns the panel specified or null if it's not in this region.
43035      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43036      * @return {Roo.ContentPanel}
43037      */
43038     getPanel : function(id){
43039         if(typeof id == "object"){ // must be panel obj
43040             return id;
43041         }
43042         return this.panels.get(id);
43043     },
43044     
43045     /**
43046      * Returns this regions position (north/south/east/west/center).
43047      * @return {String} 
43048      */
43049     getPosition: function(){
43050         return this.position;    
43051     }
43052 });/*
43053  * Based on:
43054  * Ext JS Library 1.1.1
43055  * Copyright(c) 2006-2007, Ext JS, LLC.
43056  *
43057  * Originally Released Under LGPL - original licence link has changed is not relivant.
43058  *
43059  * Fork - LGPL
43060  * <script type="text/javascript">
43061  */
43062  
43063 /**
43064  * @class Roo.bootstrap.layout.Region
43065  * @extends Roo.bootstrap.layout.Basic
43066  * This class represents a region in a layout manager.
43067  
43068  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43069  * @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})
43070  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43071  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43072  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43073  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43074  * @cfg {String}    title           The title for the region (overrides panel titles)
43075  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43076  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43077  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43078  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43079  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43080  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43081  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43082  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43083  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43084  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43085
43086  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43087  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43088  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43089  * @cfg {Number}    width           For East/West panels
43090  * @cfg {Number}    height          For North/South panels
43091  * @cfg {Boolean}   split           To show the splitter
43092  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43093  * 
43094  * @cfg {string}   cls             Extra CSS classes to add to region
43095  * 
43096  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43097  * @cfg {string}   region  the region that it inhabits..
43098  *
43099
43100  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43101  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43102
43103  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43104  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43105  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43106  */
43107 Roo.bootstrap.layout.Region = function(config)
43108 {
43109     this.applyConfig(config);
43110
43111     var mgr = config.mgr;
43112     var pos = config.region;
43113     config.skipConfig = true;
43114     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43115     
43116     if (mgr.el) {
43117         this.onRender(mgr.el);   
43118     }
43119      
43120     this.visible = true;
43121     this.collapsed = false;
43122     this.unrendered_panels = [];
43123 };
43124
43125 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43126
43127     position: '', // set by wrapper (eg. north/south etc..)
43128     unrendered_panels : null,  // unrendered panels.
43129     
43130     tabPosition : false,
43131     
43132     mgr: false, // points to 'Border'
43133     
43134     
43135     createBody : function(){
43136         /** This region's body element 
43137         * @type Roo.Element */
43138         this.bodyEl = this.el.createChild({
43139                 tag: "div",
43140                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43141         });
43142     },
43143
43144     onRender: function(ctr, pos)
43145     {
43146         var dh = Roo.DomHelper;
43147         /** This region's container element 
43148         * @type Roo.Element */
43149         this.el = dh.append(ctr.dom, {
43150                 tag: "div",
43151                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43152             }, true);
43153         /** This region's title element 
43154         * @type Roo.Element */
43155     
43156         this.titleEl = dh.append(this.el.dom,  {
43157                 tag: "div",
43158                 unselectable: "on",
43159                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43160                 children:[
43161                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43162                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43163                 ]
43164             }, true);
43165         
43166         this.titleEl.enableDisplayMode();
43167         /** This region's title text element 
43168         * @type HTMLElement */
43169         this.titleTextEl = this.titleEl.dom.firstChild;
43170         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43171         /*
43172         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43173         this.closeBtn.enableDisplayMode();
43174         this.closeBtn.on("click", this.closeClicked, this);
43175         this.closeBtn.hide();
43176     */
43177         this.createBody(this.config);
43178         if(this.config.hideWhenEmpty){
43179             this.hide();
43180             this.on("paneladded", this.validateVisibility, this);
43181             this.on("panelremoved", this.validateVisibility, this);
43182         }
43183         if(this.autoScroll){
43184             this.bodyEl.setStyle("overflow", "auto");
43185         }else{
43186             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43187         }
43188         //if(c.titlebar !== false){
43189             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43190                 this.titleEl.hide();
43191             }else{
43192                 this.titleEl.show();
43193                 if(this.config.title){
43194                     this.titleTextEl.innerHTML = this.config.title;
43195                 }
43196             }
43197         //}
43198         if(this.config.collapsed){
43199             this.collapse(true);
43200         }
43201         if(this.config.hidden){
43202             this.hide();
43203         }
43204         
43205         if (this.unrendered_panels && this.unrendered_panels.length) {
43206             for (var i =0;i< this.unrendered_panels.length; i++) {
43207                 this.add(this.unrendered_panels[i]);
43208             }
43209             this.unrendered_panels = null;
43210             
43211         }
43212         
43213     },
43214     
43215     applyConfig : function(c)
43216     {
43217         /*
43218          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43219             var dh = Roo.DomHelper;
43220             if(c.titlebar !== false){
43221                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43222                 this.collapseBtn.on("click", this.collapse, this);
43223                 this.collapseBtn.enableDisplayMode();
43224                 /*
43225                 if(c.showPin === true || this.showPin){
43226                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43227                     this.stickBtn.enableDisplayMode();
43228                     this.stickBtn.on("click", this.expand, this);
43229                     this.stickBtn.hide();
43230                 }
43231                 
43232             }
43233             */
43234             /** This region's collapsed element
43235             * @type Roo.Element */
43236             /*
43237              *
43238             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43239                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43240             ]}, true);
43241             
43242             if(c.floatable !== false){
43243                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43244                this.collapsedEl.on("click", this.collapseClick, this);
43245             }
43246
43247             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43248                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43249                    id: "message", unselectable: "on", style:{"float":"left"}});
43250                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43251              }
43252             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43253             this.expandBtn.on("click", this.expand, this);
43254             
43255         }
43256         
43257         if(this.collapseBtn){
43258             this.collapseBtn.setVisible(c.collapsible == true);
43259         }
43260         
43261         this.cmargins = c.cmargins || this.cmargins ||
43262                          (this.position == "west" || this.position == "east" ?
43263                              {top: 0, left: 2, right:2, bottom: 0} :
43264                              {top: 2, left: 0, right:0, bottom: 2});
43265         */
43266         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43267         
43268         
43269         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43270         
43271         this.autoScroll = c.autoScroll || false;
43272         
43273         
43274        
43275         
43276         this.duration = c.duration || .30;
43277         this.slideDuration = c.slideDuration || .45;
43278         this.config = c;
43279        
43280     },
43281     /**
43282      * Returns true if this region is currently visible.
43283      * @return {Boolean}
43284      */
43285     isVisible : function(){
43286         return this.visible;
43287     },
43288
43289     /**
43290      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43291      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43292      */
43293     //setCollapsedTitle : function(title){
43294     //    title = title || "&#160;";
43295      //   if(this.collapsedTitleTextEl){
43296       //      this.collapsedTitleTextEl.innerHTML = title;
43297        // }
43298     //},
43299
43300     getBox : function(){
43301         var b;
43302       //  if(!this.collapsed){
43303             b = this.el.getBox(false, true);
43304        // }else{
43305           //  b = this.collapsedEl.getBox(false, true);
43306         //}
43307         return b;
43308     },
43309
43310     getMargins : function(){
43311         return this.margins;
43312         //return this.collapsed ? this.cmargins : this.margins;
43313     },
43314 /*
43315     highlight : function(){
43316         this.el.addClass("x-layout-panel-dragover");
43317     },
43318
43319     unhighlight : function(){
43320         this.el.removeClass("x-layout-panel-dragover");
43321     },
43322 */
43323     updateBox : function(box)
43324     {
43325         if (!this.bodyEl) {
43326             return; // not rendered yet..
43327         }
43328         
43329         this.box = box;
43330         if(!this.collapsed){
43331             this.el.dom.style.left = box.x + "px";
43332             this.el.dom.style.top = box.y + "px";
43333             this.updateBody(box.width, box.height);
43334         }else{
43335             this.collapsedEl.dom.style.left = box.x + "px";
43336             this.collapsedEl.dom.style.top = box.y + "px";
43337             this.collapsedEl.setSize(box.width, box.height);
43338         }
43339         if(this.tabs){
43340             this.tabs.autoSizeTabs();
43341         }
43342     },
43343
43344     updateBody : function(w, h)
43345     {
43346         if(w !== null){
43347             this.el.setWidth(w);
43348             w -= this.el.getBorderWidth("rl");
43349             if(this.config.adjustments){
43350                 w += this.config.adjustments[0];
43351             }
43352         }
43353         if(h !== null && h > 0){
43354             this.el.setHeight(h);
43355             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43356             h -= this.el.getBorderWidth("tb");
43357             if(this.config.adjustments){
43358                 h += this.config.adjustments[1];
43359             }
43360             this.bodyEl.setHeight(h);
43361             if(this.tabs){
43362                 h = this.tabs.syncHeight(h);
43363             }
43364         }
43365         if(this.panelSize){
43366             w = w !== null ? w : this.panelSize.width;
43367             h = h !== null ? h : this.panelSize.height;
43368         }
43369         if(this.activePanel){
43370             var el = this.activePanel.getEl();
43371             w = w !== null ? w : el.getWidth();
43372             h = h !== null ? h : el.getHeight();
43373             this.panelSize = {width: w, height: h};
43374             this.activePanel.setSize(w, h);
43375         }
43376         if(Roo.isIE && this.tabs){
43377             this.tabs.el.repaint();
43378         }
43379     },
43380
43381     /**
43382      * Returns the container element for this region.
43383      * @return {Roo.Element}
43384      */
43385     getEl : function(){
43386         return this.el;
43387     },
43388
43389     /**
43390      * Hides this region.
43391      */
43392     hide : function(){
43393         //if(!this.collapsed){
43394             this.el.dom.style.left = "-2000px";
43395             this.el.hide();
43396         //}else{
43397          //   this.collapsedEl.dom.style.left = "-2000px";
43398          //   this.collapsedEl.hide();
43399        // }
43400         this.visible = false;
43401         this.fireEvent("visibilitychange", this, false);
43402     },
43403
43404     /**
43405      * Shows this region if it was previously hidden.
43406      */
43407     show : function(){
43408         //if(!this.collapsed){
43409             this.el.show();
43410         //}else{
43411         //    this.collapsedEl.show();
43412        // }
43413         this.visible = true;
43414         this.fireEvent("visibilitychange", this, true);
43415     },
43416 /*
43417     closeClicked : function(){
43418         if(this.activePanel){
43419             this.remove(this.activePanel);
43420         }
43421     },
43422
43423     collapseClick : function(e){
43424         if(this.isSlid){
43425            e.stopPropagation();
43426            this.slideIn();
43427         }else{
43428            e.stopPropagation();
43429            this.slideOut();
43430         }
43431     },
43432 */
43433     /**
43434      * Collapses this region.
43435      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43436      */
43437     /*
43438     collapse : function(skipAnim, skipCheck = false){
43439         if(this.collapsed) {
43440             return;
43441         }
43442         
43443         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43444             
43445             this.collapsed = true;
43446             if(this.split){
43447                 this.split.el.hide();
43448             }
43449             if(this.config.animate && skipAnim !== true){
43450                 this.fireEvent("invalidated", this);
43451                 this.animateCollapse();
43452             }else{
43453                 this.el.setLocation(-20000,-20000);
43454                 this.el.hide();
43455                 this.collapsedEl.show();
43456                 this.fireEvent("collapsed", this);
43457                 this.fireEvent("invalidated", this);
43458             }
43459         }
43460         
43461     },
43462 */
43463     animateCollapse : function(){
43464         // overridden
43465     },
43466
43467     /**
43468      * Expands this region if it was previously collapsed.
43469      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43470      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43471      */
43472     /*
43473     expand : function(e, skipAnim){
43474         if(e) {
43475             e.stopPropagation();
43476         }
43477         if(!this.collapsed || this.el.hasActiveFx()) {
43478             return;
43479         }
43480         if(this.isSlid){
43481             this.afterSlideIn();
43482             skipAnim = true;
43483         }
43484         this.collapsed = false;
43485         if(this.config.animate && skipAnim !== true){
43486             this.animateExpand();
43487         }else{
43488             this.el.show();
43489             if(this.split){
43490                 this.split.el.show();
43491             }
43492             this.collapsedEl.setLocation(-2000,-2000);
43493             this.collapsedEl.hide();
43494             this.fireEvent("invalidated", this);
43495             this.fireEvent("expanded", this);
43496         }
43497     },
43498 */
43499     animateExpand : function(){
43500         // overridden
43501     },
43502
43503     initTabs : function()
43504     {
43505         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43506         
43507         var ts = new Roo.bootstrap.panel.Tabs({
43508             el: this.bodyEl.dom,
43509             region : this,
43510             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
43511             disableTooltips: this.config.disableTabTips,
43512             toolbar : this.config.toolbar
43513         });
43514         
43515         if(this.config.hideTabs){
43516             ts.stripWrap.setDisplayed(false);
43517         }
43518         this.tabs = ts;
43519         ts.resizeTabs = this.config.resizeTabs === true;
43520         ts.minTabWidth = this.config.minTabWidth || 40;
43521         ts.maxTabWidth = this.config.maxTabWidth || 250;
43522         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43523         ts.monitorResize = false;
43524         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43525         ts.bodyEl.addClass('roo-layout-tabs-body');
43526         this.panels.each(this.initPanelAsTab, this);
43527     },
43528
43529     initPanelAsTab : function(panel){
43530         var ti = this.tabs.addTab(
43531             panel.getEl().id,
43532             panel.getTitle(),
43533             null,
43534             this.config.closeOnTab && panel.isClosable(),
43535             panel.tpl
43536         );
43537         if(panel.tabTip !== undefined){
43538             ti.setTooltip(panel.tabTip);
43539         }
43540         ti.on("activate", function(){
43541               this.setActivePanel(panel);
43542         }, this);
43543         
43544         if(this.config.closeOnTab){
43545             ti.on("beforeclose", function(t, e){
43546                 e.cancel = true;
43547                 this.remove(panel);
43548             }, this);
43549         }
43550         
43551         panel.tabItem = ti;
43552         
43553         return ti;
43554     },
43555
43556     updatePanelTitle : function(panel, title)
43557     {
43558         if(this.activePanel == panel){
43559             this.updateTitle(title);
43560         }
43561         if(this.tabs){
43562             var ti = this.tabs.getTab(panel.getEl().id);
43563             ti.setText(title);
43564             if(panel.tabTip !== undefined){
43565                 ti.setTooltip(panel.tabTip);
43566             }
43567         }
43568     },
43569
43570     updateTitle : function(title){
43571         if(this.titleTextEl && !this.config.title){
43572             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43573         }
43574     },
43575
43576     setActivePanel : function(panel)
43577     {
43578         panel = this.getPanel(panel);
43579         if(this.activePanel && this.activePanel != panel){
43580             if(this.activePanel.setActiveState(false) === false){
43581                 return;
43582             }
43583         }
43584         this.activePanel = panel;
43585         panel.setActiveState(true);
43586         if(this.panelSize){
43587             panel.setSize(this.panelSize.width, this.panelSize.height);
43588         }
43589         if(this.closeBtn){
43590             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43591         }
43592         this.updateTitle(panel.getTitle());
43593         if(this.tabs){
43594             this.fireEvent("invalidated", this);
43595         }
43596         this.fireEvent("panelactivated", this, panel);
43597     },
43598
43599     /**
43600      * Shows the specified panel.
43601      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43602      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43603      */
43604     showPanel : function(panel)
43605     {
43606         panel = this.getPanel(panel);
43607         if(panel){
43608             if(this.tabs){
43609                 var tab = this.tabs.getTab(panel.getEl().id);
43610                 if(tab.isHidden()){
43611                     this.tabs.unhideTab(tab.id);
43612                 }
43613                 tab.activate();
43614             }else{
43615                 this.setActivePanel(panel);
43616             }
43617         }
43618         return panel;
43619     },
43620
43621     /**
43622      * Get the active panel for this region.
43623      * @return {Roo.ContentPanel} The active panel or null
43624      */
43625     getActivePanel : function(){
43626         return this.activePanel;
43627     },
43628
43629     validateVisibility : function(){
43630         if(this.panels.getCount() < 1){
43631             this.updateTitle("&#160;");
43632             this.closeBtn.hide();
43633             this.hide();
43634         }else{
43635             if(!this.isVisible()){
43636                 this.show();
43637             }
43638         }
43639     },
43640
43641     /**
43642      * Adds the passed ContentPanel(s) to this region.
43643      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43644      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43645      */
43646     add : function(panel)
43647     {
43648         if(arguments.length > 1){
43649             for(var i = 0, len = arguments.length; i < len; i++) {
43650                 this.add(arguments[i]);
43651             }
43652             return null;
43653         }
43654         
43655         // if we have not been rendered yet, then we can not really do much of this..
43656         if (!this.bodyEl) {
43657             this.unrendered_panels.push(panel);
43658             return panel;
43659         }
43660         
43661         
43662         
43663         
43664         if(this.hasPanel(panel)){
43665             this.showPanel(panel);
43666             return panel;
43667         }
43668         panel.setRegion(this);
43669         this.panels.add(panel);
43670        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43671             // sinle panel - no tab...?? would it not be better to render it with the tabs,
43672             // and hide them... ???
43673             this.bodyEl.dom.appendChild(panel.getEl().dom);
43674             if(panel.background !== true){
43675                 this.setActivePanel(panel);
43676             }
43677             this.fireEvent("paneladded", this, panel);
43678             return panel;
43679         }
43680         */
43681         if(!this.tabs){
43682             this.initTabs();
43683         }else{
43684             this.initPanelAsTab(panel);
43685         }
43686         
43687         
43688         if(panel.background !== true){
43689             this.tabs.activate(panel.getEl().id);
43690         }
43691         this.fireEvent("paneladded", this, panel);
43692         return panel;
43693     },
43694
43695     /**
43696      * Hides the tab for the specified panel.
43697      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43698      */
43699     hidePanel : function(panel){
43700         if(this.tabs && (panel = this.getPanel(panel))){
43701             this.tabs.hideTab(panel.getEl().id);
43702         }
43703     },
43704
43705     /**
43706      * Unhides the tab for a previously hidden panel.
43707      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43708      */
43709     unhidePanel : function(panel){
43710         if(this.tabs && (panel = this.getPanel(panel))){
43711             this.tabs.unhideTab(panel.getEl().id);
43712         }
43713     },
43714
43715     clearPanels : function(){
43716         while(this.panels.getCount() > 0){
43717              this.remove(this.panels.first());
43718         }
43719     },
43720
43721     /**
43722      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43723      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43724      * @param {Boolean} preservePanel Overrides the config preservePanel option
43725      * @return {Roo.ContentPanel} The panel that was removed
43726      */
43727     remove : function(panel, preservePanel)
43728     {
43729         panel = this.getPanel(panel);
43730         if(!panel){
43731             return null;
43732         }
43733         var e = {};
43734         this.fireEvent("beforeremove", this, panel, e);
43735         if(e.cancel === true){
43736             return null;
43737         }
43738         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43739         var panelId = panel.getId();
43740         this.panels.removeKey(panelId);
43741         if(preservePanel){
43742             document.body.appendChild(panel.getEl().dom);
43743         }
43744         if(this.tabs){
43745             this.tabs.removeTab(panel.getEl().id);
43746         }else if (!preservePanel){
43747             this.bodyEl.dom.removeChild(panel.getEl().dom);
43748         }
43749         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43750             var p = this.panels.first();
43751             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43752             tempEl.appendChild(p.getEl().dom);
43753             this.bodyEl.update("");
43754             this.bodyEl.dom.appendChild(p.getEl().dom);
43755             tempEl = null;
43756             this.updateTitle(p.getTitle());
43757             this.tabs = null;
43758             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43759             this.setActivePanel(p);
43760         }
43761         panel.setRegion(null);
43762         if(this.activePanel == panel){
43763             this.activePanel = null;
43764         }
43765         if(this.config.autoDestroy !== false && preservePanel !== true){
43766             try{panel.destroy();}catch(e){}
43767         }
43768         this.fireEvent("panelremoved", this, panel);
43769         return panel;
43770     },
43771
43772     /**
43773      * Returns the TabPanel component used by this region
43774      * @return {Roo.TabPanel}
43775      */
43776     getTabs : function(){
43777         return this.tabs;
43778     },
43779
43780     createTool : function(parentEl, className){
43781         var btn = Roo.DomHelper.append(parentEl, {
43782             tag: "div",
43783             cls: "x-layout-tools-button",
43784             children: [ {
43785                 tag: "div",
43786                 cls: "roo-layout-tools-button-inner " + className,
43787                 html: "&#160;"
43788             }]
43789         }, true);
43790         btn.addClassOnOver("roo-layout-tools-button-over");
43791         return btn;
43792     }
43793 });/*
43794  * Based on:
43795  * Ext JS Library 1.1.1
43796  * Copyright(c) 2006-2007, Ext JS, LLC.
43797  *
43798  * Originally Released Under LGPL - original licence link has changed is not relivant.
43799  *
43800  * Fork - LGPL
43801  * <script type="text/javascript">
43802  */
43803  
43804
43805
43806 /**
43807  * @class Roo.SplitLayoutRegion
43808  * @extends Roo.LayoutRegion
43809  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43810  */
43811 Roo.bootstrap.layout.Split = function(config){
43812     this.cursor = config.cursor;
43813     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
43814 };
43815
43816 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
43817 {
43818     splitTip : "Drag to resize.",
43819     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43820     useSplitTips : false,
43821
43822     applyConfig : function(config){
43823         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
43824     },
43825     
43826     onRender : function(ctr,pos) {
43827         
43828         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
43829         if(!this.config.split){
43830             return;
43831         }
43832         if(!this.split){
43833             
43834             var splitEl = Roo.DomHelper.append(ctr.dom,  {
43835                             tag: "div",
43836                             id: this.el.id + "-split",
43837                             cls: "roo-layout-split roo-layout-split-"+this.position,
43838                             html: "&#160;"
43839             });
43840             /** The SplitBar for this region 
43841             * @type Roo.SplitBar */
43842             // does not exist yet...
43843             Roo.log([this.position, this.orientation]);
43844             
43845             this.split = new Roo.bootstrap.SplitBar({
43846                 dragElement : splitEl,
43847                 resizingElement: this.el,
43848                 orientation : this.orientation
43849             });
43850             
43851             this.split.on("moved", this.onSplitMove, this);
43852             this.split.useShim = this.config.useShim === true;
43853             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43854             if(this.useSplitTips){
43855                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43856             }
43857             //if(config.collapsible){
43858             //    this.split.el.on("dblclick", this.collapse,  this);
43859             //}
43860         }
43861         if(typeof this.config.minSize != "undefined"){
43862             this.split.minSize = this.config.minSize;
43863         }
43864         if(typeof this.config.maxSize != "undefined"){
43865             this.split.maxSize = this.config.maxSize;
43866         }
43867         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
43868             this.hideSplitter();
43869         }
43870         
43871     },
43872
43873     getHMaxSize : function(){
43874          var cmax = this.config.maxSize || 10000;
43875          var center = this.mgr.getRegion("center");
43876          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43877     },
43878
43879     getVMaxSize : function(){
43880          var cmax = this.config.maxSize || 10000;
43881          var center = this.mgr.getRegion("center");
43882          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43883     },
43884
43885     onSplitMove : function(split, newSize){
43886         this.fireEvent("resized", this, newSize);
43887     },
43888     
43889     /** 
43890      * Returns the {@link Roo.SplitBar} for this region.
43891      * @return {Roo.SplitBar}
43892      */
43893     getSplitBar : function(){
43894         return this.split;
43895     },
43896     
43897     hide : function(){
43898         this.hideSplitter();
43899         Roo.bootstrap.layout.Split.superclass.hide.call(this);
43900     },
43901
43902     hideSplitter : function(){
43903         if(this.split){
43904             this.split.el.setLocation(-2000,-2000);
43905             this.split.el.hide();
43906         }
43907     },
43908
43909     show : function(){
43910         if(this.split){
43911             this.split.el.show();
43912         }
43913         Roo.bootstrap.layout.Split.superclass.show.call(this);
43914     },
43915     
43916     beforeSlide: function(){
43917         if(Roo.isGecko){// firefox overflow auto bug workaround
43918             this.bodyEl.clip();
43919             if(this.tabs) {
43920                 this.tabs.bodyEl.clip();
43921             }
43922             if(this.activePanel){
43923                 this.activePanel.getEl().clip();
43924                 
43925                 if(this.activePanel.beforeSlide){
43926                     this.activePanel.beforeSlide();
43927                 }
43928             }
43929         }
43930     },
43931     
43932     afterSlide : function(){
43933         if(Roo.isGecko){// firefox overflow auto bug workaround
43934             this.bodyEl.unclip();
43935             if(this.tabs) {
43936                 this.tabs.bodyEl.unclip();
43937             }
43938             if(this.activePanel){
43939                 this.activePanel.getEl().unclip();
43940                 if(this.activePanel.afterSlide){
43941                     this.activePanel.afterSlide();
43942                 }
43943             }
43944         }
43945     },
43946
43947     initAutoHide : function(){
43948         if(this.autoHide !== false){
43949             if(!this.autoHideHd){
43950                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43951                 this.autoHideHd = {
43952                     "mouseout": function(e){
43953                         if(!e.within(this.el, true)){
43954                             st.delay(500);
43955                         }
43956                     },
43957                     "mouseover" : function(e){
43958                         st.cancel();
43959                     },
43960                     scope : this
43961                 };
43962             }
43963             this.el.on(this.autoHideHd);
43964         }
43965     },
43966
43967     clearAutoHide : function(){
43968         if(this.autoHide !== false){
43969             this.el.un("mouseout", this.autoHideHd.mouseout);
43970             this.el.un("mouseover", this.autoHideHd.mouseover);
43971         }
43972     },
43973
43974     clearMonitor : function(){
43975         Roo.get(document).un("click", this.slideInIf, this);
43976     },
43977
43978     // these names are backwards but not changed for compat
43979     slideOut : function(){
43980         if(this.isSlid || this.el.hasActiveFx()){
43981             return;
43982         }
43983         this.isSlid = true;
43984         if(this.collapseBtn){
43985             this.collapseBtn.hide();
43986         }
43987         this.closeBtnState = this.closeBtn.getStyle('display');
43988         this.closeBtn.hide();
43989         if(this.stickBtn){
43990             this.stickBtn.show();
43991         }
43992         this.el.show();
43993         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43994         this.beforeSlide();
43995         this.el.setStyle("z-index", 10001);
43996         this.el.slideIn(this.getSlideAnchor(), {
43997             callback: function(){
43998                 this.afterSlide();
43999                 this.initAutoHide();
44000                 Roo.get(document).on("click", this.slideInIf, this);
44001                 this.fireEvent("slideshow", this);
44002             },
44003             scope: this,
44004             block: true
44005         });
44006     },
44007
44008     afterSlideIn : function(){
44009         this.clearAutoHide();
44010         this.isSlid = false;
44011         this.clearMonitor();
44012         this.el.setStyle("z-index", "");
44013         if(this.collapseBtn){
44014             this.collapseBtn.show();
44015         }
44016         this.closeBtn.setStyle('display', this.closeBtnState);
44017         if(this.stickBtn){
44018             this.stickBtn.hide();
44019         }
44020         this.fireEvent("slidehide", this);
44021     },
44022
44023     slideIn : function(cb){
44024         if(!this.isSlid || this.el.hasActiveFx()){
44025             Roo.callback(cb);
44026             return;
44027         }
44028         this.isSlid = false;
44029         this.beforeSlide();
44030         this.el.slideOut(this.getSlideAnchor(), {
44031             callback: function(){
44032                 this.el.setLeftTop(-10000, -10000);
44033                 this.afterSlide();
44034                 this.afterSlideIn();
44035                 Roo.callback(cb);
44036             },
44037             scope: this,
44038             block: true
44039         });
44040     },
44041     
44042     slideInIf : function(e){
44043         if(!e.within(this.el)){
44044             this.slideIn();
44045         }
44046     },
44047
44048     animateCollapse : function(){
44049         this.beforeSlide();
44050         this.el.setStyle("z-index", 20000);
44051         var anchor = this.getSlideAnchor();
44052         this.el.slideOut(anchor, {
44053             callback : function(){
44054                 this.el.setStyle("z-index", "");
44055                 this.collapsedEl.slideIn(anchor, {duration:.3});
44056                 this.afterSlide();
44057                 this.el.setLocation(-10000,-10000);
44058                 this.el.hide();
44059                 this.fireEvent("collapsed", this);
44060             },
44061             scope: this,
44062             block: true
44063         });
44064     },
44065
44066     animateExpand : function(){
44067         this.beforeSlide();
44068         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44069         this.el.setStyle("z-index", 20000);
44070         this.collapsedEl.hide({
44071             duration:.1
44072         });
44073         this.el.slideIn(this.getSlideAnchor(), {
44074             callback : function(){
44075                 this.el.setStyle("z-index", "");
44076                 this.afterSlide();
44077                 if(this.split){
44078                     this.split.el.show();
44079                 }
44080                 this.fireEvent("invalidated", this);
44081                 this.fireEvent("expanded", this);
44082             },
44083             scope: this,
44084             block: true
44085         });
44086     },
44087
44088     anchors : {
44089         "west" : "left",
44090         "east" : "right",
44091         "north" : "top",
44092         "south" : "bottom"
44093     },
44094
44095     sanchors : {
44096         "west" : "l",
44097         "east" : "r",
44098         "north" : "t",
44099         "south" : "b"
44100     },
44101
44102     canchors : {
44103         "west" : "tl-tr",
44104         "east" : "tr-tl",
44105         "north" : "tl-bl",
44106         "south" : "bl-tl"
44107     },
44108
44109     getAnchor : function(){
44110         return this.anchors[this.position];
44111     },
44112
44113     getCollapseAnchor : function(){
44114         return this.canchors[this.position];
44115     },
44116
44117     getSlideAnchor : function(){
44118         return this.sanchors[this.position];
44119     },
44120
44121     getAlignAdj : function(){
44122         var cm = this.cmargins;
44123         switch(this.position){
44124             case "west":
44125                 return [0, 0];
44126             break;
44127             case "east":
44128                 return [0, 0];
44129             break;
44130             case "north":
44131                 return [0, 0];
44132             break;
44133             case "south":
44134                 return [0, 0];
44135             break;
44136         }
44137     },
44138
44139     getExpandAdj : function(){
44140         var c = this.collapsedEl, cm = this.cmargins;
44141         switch(this.position){
44142             case "west":
44143                 return [-(cm.right+c.getWidth()+cm.left), 0];
44144             break;
44145             case "east":
44146                 return [cm.right+c.getWidth()+cm.left, 0];
44147             break;
44148             case "north":
44149                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44150             break;
44151             case "south":
44152                 return [0, cm.top+cm.bottom+c.getHeight()];
44153             break;
44154         }
44155     }
44156 });/*
44157  * Based on:
44158  * Ext JS Library 1.1.1
44159  * Copyright(c) 2006-2007, Ext JS, LLC.
44160  *
44161  * Originally Released Under LGPL - original licence link has changed is not relivant.
44162  *
44163  * Fork - LGPL
44164  * <script type="text/javascript">
44165  */
44166 /*
44167  * These classes are private internal classes
44168  */
44169 Roo.bootstrap.layout.Center = function(config){
44170     config.region = "center";
44171     Roo.bootstrap.layout.Region.call(this, config);
44172     this.visible = true;
44173     this.minWidth = config.minWidth || 20;
44174     this.minHeight = config.minHeight || 20;
44175 };
44176
44177 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44178     hide : function(){
44179         // center panel can't be hidden
44180     },
44181     
44182     show : function(){
44183         // center panel can't be hidden
44184     },
44185     
44186     getMinWidth: function(){
44187         return this.minWidth;
44188     },
44189     
44190     getMinHeight: function(){
44191         return this.minHeight;
44192     }
44193 });
44194
44195
44196
44197
44198  
44199
44200
44201
44202
44203
44204
44205 Roo.bootstrap.layout.North = function(config)
44206 {
44207     config.region = 'north';
44208     config.cursor = 'n-resize';
44209     
44210     Roo.bootstrap.layout.Split.call(this, config);
44211     
44212     
44213     if(this.split){
44214         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44215         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44216         this.split.el.addClass("roo-layout-split-v");
44217     }
44218     //var size = config.initialSize || config.height;
44219     //if(this.el && typeof size != "undefined"){
44220     //    this.el.setHeight(size);
44221     //}
44222 };
44223 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44224 {
44225     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44226      
44227      
44228     onRender : function(ctr, pos)
44229     {
44230         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44231         var size = this.config.initialSize || this.config.height;
44232         if(this.el && typeof size != "undefined"){
44233             this.el.setHeight(size);
44234         }
44235     
44236     },
44237     
44238     getBox : function(){
44239         if(this.collapsed){
44240             return this.collapsedEl.getBox();
44241         }
44242         var box = this.el.getBox();
44243         if(this.split){
44244             box.height += this.split.el.getHeight();
44245         }
44246         return box;
44247     },
44248     
44249     updateBox : function(box){
44250         if(this.split && !this.collapsed){
44251             box.height -= this.split.el.getHeight();
44252             this.split.el.setLeft(box.x);
44253             this.split.el.setTop(box.y+box.height);
44254             this.split.el.setWidth(box.width);
44255         }
44256         if(this.collapsed){
44257             this.updateBody(box.width, null);
44258         }
44259         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44260     }
44261 });
44262
44263
44264
44265
44266
44267 Roo.bootstrap.layout.South = function(config){
44268     config.region = 'south';
44269     config.cursor = 's-resize';
44270     Roo.bootstrap.layout.Split.call(this, config);
44271     if(this.split){
44272         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44273         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44274         this.split.el.addClass("roo-layout-split-v");
44275     }
44276     
44277 };
44278
44279 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44280     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44281     
44282     onRender : function(ctr, pos)
44283     {
44284         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44285         var size = this.config.initialSize || this.config.height;
44286         if(this.el && typeof size != "undefined"){
44287             this.el.setHeight(size);
44288         }
44289     
44290     },
44291     
44292     getBox : function(){
44293         if(this.collapsed){
44294             return this.collapsedEl.getBox();
44295         }
44296         var box = this.el.getBox();
44297         if(this.split){
44298             var sh = this.split.el.getHeight();
44299             box.height += sh;
44300             box.y -= sh;
44301         }
44302         return box;
44303     },
44304     
44305     updateBox : function(box){
44306         if(this.split && !this.collapsed){
44307             var sh = this.split.el.getHeight();
44308             box.height -= sh;
44309             box.y += sh;
44310             this.split.el.setLeft(box.x);
44311             this.split.el.setTop(box.y-sh);
44312             this.split.el.setWidth(box.width);
44313         }
44314         if(this.collapsed){
44315             this.updateBody(box.width, null);
44316         }
44317         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44318     }
44319 });
44320
44321 Roo.bootstrap.layout.East = function(config){
44322     config.region = "east";
44323     config.cursor = "e-resize";
44324     Roo.bootstrap.layout.Split.call(this, config);
44325     if(this.split){
44326         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44327         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44328         this.split.el.addClass("roo-layout-split-h");
44329     }
44330     
44331 };
44332 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44333     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44334     
44335     onRender : function(ctr, pos)
44336     {
44337         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44338         var size = this.config.initialSize || this.config.width;
44339         if(this.el && typeof size != "undefined"){
44340             this.el.setWidth(size);
44341         }
44342     
44343     },
44344     
44345     getBox : function(){
44346         if(this.collapsed){
44347             return this.collapsedEl.getBox();
44348         }
44349         var box = this.el.getBox();
44350         if(this.split){
44351             var sw = this.split.el.getWidth();
44352             box.width += sw;
44353             box.x -= sw;
44354         }
44355         return box;
44356     },
44357
44358     updateBox : function(box){
44359         if(this.split && !this.collapsed){
44360             var sw = this.split.el.getWidth();
44361             box.width -= sw;
44362             this.split.el.setLeft(box.x);
44363             this.split.el.setTop(box.y);
44364             this.split.el.setHeight(box.height);
44365             box.x += sw;
44366         }
44367         if(this.collapsed){
44368             this.updateBody(null, box.height);
44369         }
44370         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44371     }
44372 });
44373
44374 Roo.bootstrap.layout.West = function(config){
44375     config.region = "west";
44376     config.cursor = "w-resize";
44377     
44378     Roo.bootstrap.layout.Split.call(this, config);
44379     if(this.split){
44380         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44381         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44382         this.split.el.addClass("roo-layout-split-h");
44383     }
44384     
44385 };
44386 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44387     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44388     
44389     onRender: function(ctr, pos)
44390     {
44391         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44392         var size = this.config.initialSize || this.config.width;
44393         if(typeof size != "undefined"){
44394             this.el.setWidth(size);
44395         }
44396     },
44397     
44398     getBox : function(){
44399         if(this.collapsed){
44400             return this.collapsedEl.getBox();
44401         }
44402         var box = this.el.getBox();
44403         if (box.width == 0) {
44404             box.width = this.config.width; // kludge?
44405         }
44406         if(this.split){
44407             box.width += this.split.el.getWidth();
44408         }
44409         return box;
44410     },
44411     
44412     updateBox : function(box){
44413         if(this.split && !this.collapsed){
44414             var sw = this.split.el.getWidth();
44415             box.width -= sw;
44416             this.split.el.setLeft(box.x+box.width);
44417             this.split.el.setTop(box.y);
44418             this.split.el.setHeight(box.height);
44419         }
44420         if(this.collapsed){
44421             this.updateBody(null, box.height);
44422         }
44423         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44424     }
44425 });/*
44426  * Based on:
44427  * Ext JS Library 1.1.1
44428  * Copyright(c) 2006-2007, Ext JS, LLC.
44429  *
44430  * Originally Released Under LGPL - original licence link has changed is not relivant.
44431  *
44432  * Fork - LGPL
44433  * <script type="text/javascript">
44434  */
44435 /**
44436  * @class Roo.bootstrap.paenl.Content
44437  * @extends Roo.util.Observable
44438  * @children Roo.bootstrap.Component
44439  * @parent builder Roo.bootstrap.layout.Border
44440  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44441  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44442  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44443  * @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
44444  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44445  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44446  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44447  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44448  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44449  * @cfg {String} title          The title for this panel
44450  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44451  * @cfg {String} url            Calls {@link #setUrl} with this value
44452  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44453  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44454  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44455  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44456  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44457  * @cfg {Boolean} badges render the badges
44458  * @cfg {String} cls  extra classes to use  
44459  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44460  
44461  * @constructor
44462  * Create a new ContentPanel.
44463  * @param {String/Object} config A string to set only the title or a config object
44464  
44465  */
44466 Roo.bootstrap.panel.Content = function( config){
44467     
44468     this.tpl = config.tpl || false;
44469     
44470     var el = config.el;
44471     var content = config.content;
44472
44473     if(config.autoCreate){ // xtype is available if this is called from factory
44474         el = Roo.id();
44475     }
44476     this.el = Roo.get(el);
44477     if(!this.el && config && config.autoCreate){
44478         if(typeof config.autoCreate == "object"){
44479             if(!config.autoCreate.id){
44480                 config.autoCreate.id = config.id||el;
44481             }
44482             this.el = Roo.DomHelper.append(document.body,
44483                         config.autoCreate, true);
44484         }else{
44485             var elcfg =  {
44486                 tag: "div",
44487                 cls: (config.cls || '') +
44488                     (config.background ? ' bg-' + config.background : '') +
44489                     " roo-layout-inactive-content",
44490                 id: config.id||el
44491             };
44492             if (config.iframe) {
44493                 elcfg.cn = [
44494                     {
44495                         tag : 'iframe',
44496                         style : 'border: 0px',
44497                         src : 'about:blank'
44498                     }
44499                 ];
44500             }
44501               
44502             if (config.html) {
44503                 elcfg.html = config.html;
44504                 
44505             }
44506                         
44507             this.el = Roo.DomHelper.append(document.body, elcfg , true);
44508             if (config.iframe) {
44509                 this.iframeEl = this.el.select('iframe',true).first();
44510             }
44511             
44512         }
44513     } 
44514     this.closable = false;
44515     this.loaded = false;
44516     this.active = false;
44517    
44518       
44519     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44520         
44521         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44522         
44523         this.wrapEl = this.el; //this.el.wrap();
44524         var ti = [];
44525         if (config.toolbar.items) {
44526             ti = config.toolbar.items ;
44527             delete config.toolbar.items ;
44528         }
44529         
44530         var nitems = [];
44531         this.toolbar.render(this.wrapEl, 'before');
44532         for(var i =0;i < ti.length;i++) {
44533           //  Roo.log(['add child', items[i]]);
44534             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44535         }
44536         this.toolbar.items = nitems;
44537         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44538         delete config.toolbar;
44539         
44540     }
44541     /*
44542     // xtype created footer. - not sure if will work as we normally have to render first..
44543     if (this.footer && !this.footer.el && this.footer.xtype) {
44544         if (!this.wrapEl) {
44545             this.wrapEl = this.el.wrap();
44546         }
44547     
44548         this.footer.container = this.wrapEl.createChild();
44549          
44550         this.footer = Roo.factory(this.footer, Roo);
44551         
44552     }
44553     */
44554     
44555      if(typeof config == "string"){
44556         this.title = config;
44557     }else{
44558         Roo.apply(this, config);
44559     }
44560     
44561     if(this.resizeEl){
44562         this.resizeEl = Roo.get(this.resizeEl, true);
44563     }else{
44564         this.resizeEl = this.el;
44565     }
44566     // handle view.xtype
44567     
44568  
44569     
44570     
44571     this.addEvents({
44572         /**
44573          * @event activate
44574          * Fires when this panel is activated. 
44575          * @param {Roo.ContentPanel} this
44576          */
44577         "activate" : true,
44578         /**
44579          * @event deactivate
44580          * Fires when this panel is activated. 
44581          * @param {Roo.ContentPanel} this
44582          */
44583         "deactivate" : true,
44584
44585         /**
44586          * @event resize
44587          * Fires when this panel is resized if fitToFrame is true.
44588          * @param {Roo.ContentPanel} this
44589          * @param {Number} width The width after any component adjustments
44590          * @param {Number} height The height after any component adjustments
44591          */
44592         "resize" : true,
44593         
44594          /**
44595          * @event render
44596          * Fires when this tab is created
44597          * @param {Roo.ContentPanel} this
44598          */
44599         "render" : true,
44600         
44601           /**
44602          * @event scroll
44603          * Fires when this content is scrolled
44604          * @param {Roo.ContentPanel} this
44605          * @param {Event} scrollEvent
44606          */
44607         "scroll" : true
44608         
44609         
44610         
44611     });
44612     
44613
44614     
44615     
44616     if(this.autoScroll && !this.iframe){
44617         this.resizeEl.setStyle("overflow", "auto");
44618         this.resizeEl.on('scroll', this.onScroll, this);
44619     } else {
44620         // fix randome scrolling
44621         //this.el.on('scroll', function() {
44622         //    Roo.log('fix random scolling');
44623         //    this.scrollTo('top',0); 
44624         //});
44625     }
44626     content = content || this.content;
44627     if(content){
44628         this.setContent(content);
44629     }
44630     if(config && config.url){
44631         this.setUrl(this.url, this.params, this.loadOnce);
44632     }
44633     
44634     
44635     
44636     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
44637     
44638     if (this.view && typeof(this.view.xtype) != 'undefined') {
44639         this.view.el = this.el.appendChild(document.createElement("div"));
44640         this.view = Roo.factory(this.view); 
44641         this.view.render  &&  this.view.render(false, '');  
44642     }
44643     
44644     
44645     this.fireEvent('render', this);
44646 };
44647
44648 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
44649     
44650     cls : '',
44651     background : '',
44652     
44653     tabTip : '',
44654     
44655     iframe : false,
44656     iframeEl : false,
44657     
44658     /* Resize Element - use this to work out scroll etc. */
44659     resizeEl : false,
44660     
44661     setRegion : function(region){
44662         this.region = region;
44663         this.setActiveClass(region && !this.background);
44664     },
44665     
44666     
44667     setActiveClass: function(state)
44668     {
44669         if(state){
44670            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
44671            this.el.setStyle('position','relative');
44672         }else{
44673            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
44674            this.el.setStyle('position', 'absolute');
44675         } 
44676     },
44677     
44678     /**
44679      * Returns the toolbar for this Panel if one was configured. 
44680      * @return {Roo.Toolbar} 
44681      */
44682     getToolbar : function(){
44683         return this.toolbar;
44684     },
44685     
44686     setActiveState : function(active)
44687     {
44688         this.active = active;
44689         this.setActiveClass(active);
44690         if(!active){
44691             if(this.fireEvent("deactivate", this) === false){
44692                 return false;
44693             }
44694             return true;
44695         }
44696         this.fireEvent("activate", this);
44697         return true;
44698     },
44699     /**
44700      * Updates this panel's element (not for iframe)
44701      * @param {String} content The new content
44702      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44703     */
44704     setContent : function(content, loadScripts){
44705         if (this.iframe) {
44706             return;
44707         }
44708         
44709         this.el.update(content, loadScripts);
44710     },
44711
44712     ignoreResize : function(w, h)
44713     {
44714         //return false; // always resize?
44715         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44716             return true;
44717         }else{
44718             this.lastSize = {width: w, height: h};
44719             return false;
44720         }
44721     },
44722     /**
44723      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44724      * @return {Roo.UpdateManager} The UpdateManager
44725      */
44726     getUpdateManager : function(){
44727         if (this.iframe) {
44728             return false;
44729         }
44730         return this.el.getUpdateManager();
44731     },
44732      /**
44733      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44734      * Does not work with IFRAME contents
44735      * @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:
44736 <pre><code>
44737 panel.load({
44738     url: "your-url.php",
44739     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44740     callback: yourFunction,
44741     scope: yourObject, //(optional scope)
44742     discardUrl: false,
44743     nocache: false,
44744     text: "Loading...",
44745     timeout: 30,
44746     scripts: false
44747 });
44748 </code></pre>
44749      
44750      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44751      * 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.
44752      * @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}
44753      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44754      * @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.
44755      * @return {Roo.ContentPanel} this
44756      */
44757     load : function(){
44758         
44759         if (this.iframe) {
44760             return this;
44761         }
44762         
44763         var um = this.el.getUpdateManager();
44764         um.update.apply(um, arguments);
44765         return this;
44766     },
44767
44768
44769     /**
44770      * 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.
44771      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44772      * @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)
44773      * @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)
44774      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
44775      */
44776     setUrl : function(url, params, loadOnce){
44777         if (this.iframe) {
44778             this.iframeEl.dom.src = url;
44779             return false;
44780         }
44781         
44782         if(this.refreshDelegate){
44783             this.removeListener("activate", this.refreshDelegate);
44784         }
44785         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44786         this.on("activate", this.refreshDelegate);
44787         return this.el.getUpdateManager();
44788     },
44789     
44790     _handleRefresh : function(url, params, loadOnce){
44791         if(!loadOnce || !this.loaded){
44792             var updater = this.el.getUpdateManager();
44793             updater.update(url, params, this._setLoaded.createDelegate(this));
44794         }
44795     },
44796     
44797     _setLoaded : function(){
44798         this.loaded = true;
44799     }, 
44800     
44801     /**
44802      * Returns this panel's id
44803      * @return {String} 
44804      */
44805     getId : function(){
44806         return this.el.id;
44807     },
44808     
44809     /** 
44810      * Returns this panel's element - used by regiosn to add.
44811      * @return {Roo.Element} 
44812      */
44813     getEl : function(){
44814         return this.wrapEl || this.el;
44815     },
44816     
44817    
44818     
44819     adjustForComponents : function(width, height)
44820     {
44821         //Roo.log('adjustForComponents ');
44822         if(this.resizeEl != this.el){
44823             width -= this.el.getFrameWidth('lr');
44824             height -= this.el.getFrameWidth('tb');
44825         }
44826         if(this.toolbar){
44827             var te = this.toolbar.getEl();
44828             te.setWidth(width);
44829             height -= te.getHeight();
44830         }
44831         if(this.footer){
44832             var te = this.footer.getEl();
44833             te.setWidth(width);
44834             height -= te.getHeight();
44835         }
44836         
44837         
44838         if(this.adjustments){
44839             width += this.adjustments[0];
44840             height += this.adjustments[1];
44841         }
44842         return {"width": width, "height": height};
44843     },
44844     
44845     setSize : function(width, height){
44846         if(this.fitToFrame && !this.ignoreResize(width, height)){
44847             if(this.fitContainer && this.resizeEl != this.el){
44848                 this.el.setSize(width, height);
44849             }
44850             var size = this.adjustForComponents(width, height);
44851             if (this.iframe) {
44852                 this.iframeEl.setSize(width,height);
44853             }
44854             
44855             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44856             this.fireEvent('resize', this, size.width, size.height);
44857             
44858             
44859         }
44860     },
44861     
44862     /**
44863      * Returns this panel's title
44864      * @return {String} 
44865      */
44866     getTitle : function(){
44867         
44868         if (typeof(this.title) != 'object') {
44869             return this.title;
44870         }
44871         
44872         var t = '';
44873         for (var k in this.title) {
44874             if (!this.title.hasOwnProperty(k)) {
44875                 continue;
44876             }
44877             
44878             if (k.indexOf('-') >= 0) {
44879                 var s = k.split('-');
44880                 for (var i = 0; i<s.length; i++) {
44881                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
44882                 }
44883             } else {
44884                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
44885             }
44886         }
44887         return t;
44888     },
44889     
44890     /**
44891      * Set this panel's title
44892      * @param {String} title
44893      */
44894     setTitle : function(title){
44895         this.title = title;
44896         if(this.region){
44897             this.region.updatePanelTitle(this, title);
44898         }
44899     },
44900     
44901     /**
44902      * Returns true is this panel was configured to be closable
44903      * @return {Boolean} 
44904      */
44905     isClosable : function(){
44906         return this.closable;
44907     },
44908     
44909     beforeSlide : function(){
44910         this.el.clip();
44911         this.resizeEl.clip();
44912     },
44913     
44914     afterSlide : function(){
44915         this.el.unclip();
44916         this.resizeEl.unclip();
44917     },
44918     
44919     /**
44920      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44921      *   Will fail silently if the {@link #setUrl} method has not been called.
44922      *   This does not activate the panel, just updates its content.
44923      */
44924     refresh : function(){
44925         if(this.refreshDelegate){
44926            this.loaded = false;
44927            this.refreshDelegate();
44928         }
44929     },
44930     
44931     /**
44932      * Destroys this panel
44933      */
44934     destroy : function(){
44935         this.el.removeAllListeners();
44936         var tempEl = document.createElement("span");
44937         tempEl.appendChild(this.el.dom);
44938         tempEl.innerHTML = "";
44939         this.el.remove();
44940         this.el = null;
44941     },
44942     
44943     /**
44944      * form - if the content panel contains a form - this is a reference to it.
44945      * @type {Roo.form.Form}
44946      */
44947     form : false,
44948     /**
44949      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
44950      *    This contains a reference to it.
44951      * @type {Roo.View}
44952      */
44953     view : false,
44954     
44955       /**
44956      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44957      * <pre><code>
44958
44959 layout.addxtype({
44960        xtype : 'Form',
44961        items: [ .... ]
44962    }
44963 );
44964
44965 </code></pre>
44966      * @param {Object} cfg Xtype definition of item to add.
44967      */
44968     
44969     
44970     getChildContainer: function () {
44971         return this.getEl();
44972     },
44973     
44974     
44975     onScroll : function(e)
44976     {
44977         this.fireEvent('scroll', this, e);
44978     }
44979     
44980     
44981     /*
44982         var  ret = new Roo.factory(cfg);
44983         return ret;
44984         
44985         
44986         // add form..
44987         if (cfg.xtype.match(/^Form$/)) {
44988             
44989             var el;
44990             //if (this.footer) {
44991             //    el = this.footer.container.insertSibling(false, 'before');
44992             //} else {
44993                 el = this.el.createChild();
44994             //}
44995
44996             this.form = new  Roo.form.Form(cfg);
44997             
44998             
44999             if ( this.form.allItems.length) {
45000                 this.form.render(el.dom);
45001             }
45002             return this.form;
45003         }
45004         // should only have one of theses..
45005         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45006             // views.. should not be just added - used named prop 'view''
45007             
45008             cfg.el = this.el.appendChild(document.createElement("div"));
45009             // factory?
45010             
45011             var ret = new Roo.factory(cfg);
45012              
45013              ret.render && ret.render(false, ''); // render blank..
45014             this.view = ret;
45015             return ret;
45016         }
45017         return false;
45018     }
45019     \*/
45020 });
45021  
45022 /**
45023  * @class Roo.bootstrap.panel.Grid
45024  * @extends Roo.bootstrap.panel.Content
45025  * @constructor
45026  * Create a new GridPanel.
45027  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45028  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45029  * @param {Object} config A the config object
45030   
45031  */
45032
45033
45034
45035 Roo.bootstrap.panel.Grid = function(config)
45036 {
45037     
45038       
45039     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45040         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45041
45042     config.el = this.wrapper;
45043     //this.el = this.wrapper;
45044     
45045       if (config.container) {
45046         // ctor'ed from a Border/panel.grid
45047         
45048         
45049         this.wrapper.setStyle("overflow", "hidden");
45050         this.wrapper.addClass('roo-grid-container');
45051
45052     }
45053     
45054     
45055     if(config.toolbar){
45056         var tool_el = this.wrapper.createChild();    
45057         this.toolbar = Roo.factory(config.toolbar);
45058         var ti = [];
45059         if (config.toolbar.items) {
45060             ti = config.toolbar.items ;
45061             delete config.toolbar.items ;
45062         }
45063         
45064         var nitems = [];
45065         this.toolbar.render(tool_el);
45066         for(var i =0;i < ti.length;i++) {
45067           //  Roo.log(['add child', items[i]]);
45068             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45069         }
45070         this.toolbar.items = nitems;
45071         
45072         delete config.toolbar;
45073     }
45074     
45075     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45076     config.grid.scrollBody = true;;
45077     config.grid.monitorWindowResize = false; // turn off autosizing
45078     config.grid.autoHeight = false;
45079     config.grid.autoWidth = false;
45080     
45081     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45082     
45083     if (config.background) {
45084         // render grid on panel activation (if panel background)
45085         this.on('activate', function(gp) {
45086             if (!gp.grid.rendered) {
45087                 gp.grid.render(this.wrapper);
45088                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45089             }
45090         });
45091             
45092     } else {
45093         this.grid.render(this.wrapper);
45094         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45095
45096     }
45097     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45098     // ??? needed ??? config.el = this.wrapper;
45099     
45100     
45101     
45102   
45103     // xtype created footer. - not sure if will work as we normally have to render first..
45104     if (this.footer && !this.footer.el && this.footer.xtype) {
45105         
45106         var ctr = this.grid.getView().getFooterPanel(true);
45107         this.footer.dataSource = this.grid.dataSource;
45108         this.footer = Roo.factory(this.footer, Roo);
45109         this.footer.render(ctr);
45110         
45111     }
45112     
45113     
45114     
45115     
45116      
45117 };
45118
45119 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45120 {
45121   
45122     getId : function(){
45123         return this.grid.id;
45124     },
45125     
45126     /**
45127      * Returns the grid for this panel
45128      * @return {Roo.bootstrap.Table} 
45129      */
45130     getGrid : function(){
45131         return this.grid;    
45132     },
45133     
45134     setSize : function(width, height)
45135     {
45136      
45137         //if(!this.ignoreResize(width, height)){
45138             var grid = this.grid;
45139             var size = this.adjustForComponents(width, height);
45140             // tfoot is not a footer?
45141           
45142             
45143             var gridel = grid.getGridEl();
45144             gridel.setSize(size.width, size.height);
45145             
45146             var tbd = grid.getGridEl().select('tbody', true).first();
45147             var thd = grid.getGridEl().select('thead',true).first();
45148             var tbf= grid.getGridEl().select('tfoot', true).first();
45149
45150             if (tbf) {
45151                 size.height -= tbf.getHeight();
45152             }
45153             if (thd) {
45154                 size.height -= thd.getHeight();
45155             }
45156             
45157             tbd.setSize(size.width, size.height );
45158             // this is for the account management tab -seems to work there.
45159             var thd = grid.getGridEl().select('thead',true).first();
45160             //if (tbd) {
45161             //    tbd.setSize(size.width, size.height - thd.getHeight());
45162             //}
45163              
45164             grid.autoSize();
45165         //}
45166    
45167     },
45168      
45169     
45170     
45171     beforeSlide : function(){
45172         this.grid.getView().scroller.clip();
45173     },
45174     
45175     afterSlide : function(){
45176         this.grid.getView().scroller.unclip();
45177     },
45178     
45179     destroy : function(){
45180         this.grid.destroy();
45181         delete this.grid;
45182         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45183     }
45184 });
45185
45186 /**
45187  * @class Roo.bootstrap.panel.Nest
45188  * @extends Roo.bootstrap.panel.Content
45189  * @constructor
45190  * Create a new Panel, that can contain a layout.Border.
45191  * 
45192  * 
45193  * @param {String/Object} config A string to set only the title or a config object
45194  */
45195 Roo.bootstrap.panel.Nest = function(config)
45196 {
45197     // construct with only one argument..
45198     /* FIXME - implement nicer consturctors
45199     if (layout.layout) {
45200         config = layout;
45201         layout = config.layout;
45202         delete config.layout;
45203     }
45204     if (layout.xtype && !layout.getEl) {
45205         // then layout needs constructing..
45206         layout = Roo.factory(layout, Roo);
45207     }
45208     */
45209     
45210     config.el =  config.layout.getEl();
45211     
45212     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45213     
45214     config.layout.monitorWindowResize = false; // turn off autosizing
45215     this.layout = config.layout;
45216     this.layout.getEl().addClass("roo-layout-nested-layout");
45217     this.layout.parent = this;
45218     
45219     
45220     
45221     
45222 };
45223
45224 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45225     /**
45226     * @cfg {Roo.BorderLayout} layout The layout for this panel
45227     */
45228     layout : false,
45229
45230     setSize : function(width, height){
45231         if(!this.ignoreResize(width, height)){
45232             var size = this.adjustForComponents(width, height);
45233             var el = this.layout.getEl();
45234             if (size.height < 1) {
45235                 el.setWidth(size.width);   
45236             } else {
45237                 el.setSize(size.width, size.height);
45238             }
45239             var touch = el.dom.offsetWidth;
45240             this.layout.layout();
45241             // ie requires a double layout on the first pass
45242             if(Roo.isIE && !this.initialized){
45243                 this.initialized = true;
45244                 this.layout.layout();
45245             }
45246         }
45247     },
45248     
45249     // activate all subpanels if not currently active..
45250     
45251     setActiveState : function(active){
45252         this.active = active;
45253         this.setActiveClass(active);
45254         
45255         if(!active){
45256             this.fireEvent("deactivate", this);
45257             return;
45258         }
45259         
45260         this.fireEvent("activate", this);
45261         // not sure if this should happen before or after..
45262         if (!this.layout) {
45263             return; // should not happen..
45264         }
45265         var reg = false;
45266         for (var r in this.layout.regions) {
45267             reg = this.layout.getRegion(r);
45268             if (reg.getActivePanel()) {
45269                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45270                 reg.setActivePanel(reg.getActivePanel());
45271                 continue;
45272             }
45273             if (!reg.panels.length) {
45274                 continue;
45275             }
45276             reg.showPanel(reg.getPanel(0));
45277         }
45278         
45279         
45280         
45281         
45282     },
45283     
45284     /**
45285      * Returns the nested BorderLayout for this panel
45286      * @return {Roo.BorderLayout} 
45287      */
45288     getLayout : function(){
45289         return this.layout;
45290     },
45291     
45292      /**
45293      * Adds a xtype elements to the layout of the nested panel
45294      * <pre><code>
45295
45296 panel.addxtype({
45297        xtype : 'ContentPanel',
45298        region: 'west',
45299        items: [ .... ]
45300    }
45301 );
45302
45303 panel.addxtype({
45304         xtype : 'NestedLayoutPanel',
45305         region: 'west',
45306         layout: {
45307            center: { },
45308            west: { }   
45309         },
45310         items : [ ... list of content panels or nested layout panels.. ]
45311    }
45312 );
45313 </code></pre>
45314      * @param {Object} cfg Xtype definition of item to add.
45315      */
45316     addxtype : function(cfg) {
45317         return this.layout.addxtype(cfg);
45318     
45319     }
45320 });/*
45321  * Based on:
45322  * Ext JS Library 1.1.1
45323  * Copyright(c) 2006-2007, Ext JS, LLC.
45324  *
45325  * Originally Released Under LGPL - original licence link has changed is not relivant.
45326  *
45327  * Fork - LGPL
45328  * <script type="text/javascript">
45329  */
45330 /**
45331  * @class Roo.TabPanel
45332  * @extends Roo.util.Observable
45333  * A lightweight tab container.
45334  * <br><br>
45335  * Usage:
45336  * <pre><code>
45337 // basic tabs 1, built from existing content
45338 var tabs = new Roo.TabPanel("tabs1");
45339 tabs.addTab("script", "View Script");
45340 tabs.addTab("markup", "View Markup");
45341 tabs.activate("script");
45342
45343 // more advanced tabs, built from javascript
45344 var jtabs = new Roo.TabPanel("jtabs");
45345 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45346
45347 // set up the UpdateManager
45348 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45349 var updater = tab2.getUpdateManager();
45350 updater.setDefaultUrl("ajax1.htm");
45351 tab2.on('activate', updater.refresh, updater, true);
45352
45353 // Use setUrl for Ajax loading
45354 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45355 tab3.setUrl("ajax2.htm", null, true);
45356
45357 // Disabled tab
45358 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45359 tab4.disable();
45360
45361 jtabs.activate("jtabs-1");
45362  * </code></pre>
45363  * @constructor
45364  * Create a new TabPanel.
45365  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45366  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45367  */
45368 Roo.bootstrap.panel.Tabs = function(config){
45369     /**
45370     * The container element for this TabPanel.
45371     * @type Roo.Element
45372     */
45373     this.el = Roo.get(config.el);
45374     delete config.el;
45375     if(config){
45376         if(typeof config == "boolean"){
45377             this.tabPosition = config ? "bottom" : "top";
45378         }else{
45379             Roo.apply(this, config);
45380         }
45381     }
45382     
45383     if(this.tabPosition == "bottom"){
45384         // if tabs are at the bottom = create the body first.
45385         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45386         this.el.addClass("roo-tabs-bottom");
45387     }
45388     // next create the tabs holders
45389     
45390     if (this.tabPosition == "west"){
45391         
45392         var reg = this.region; // fake it..
45393         while (reg) {
45394             if (!reg.mgr.parent) {
45395                 break;
45396             }
45397             reg = reg.mgr.parent.region;
45398         }
45399         Roo.log("got nest?");
45400         Roo.log(reg);
45401         if (reg.mgr.getRegion('west')) {
45402             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45403             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45404             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45405             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45406             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45407         
45408             
45409         }
45410         
45411         
45412     } else {
45413      
45414         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45415         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45416         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45417         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45418     }
45419     
45420     
45421     if(Roo.isIE){
45422         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45423     }
45424     
45425     // finally - if tabs are at the top, then create the body last..
45426     if(this.tabPosition != "bottom"){
45427         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45428          * @type Roo.Element
45429          */
45430         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45431         this.el.addClass("roo-tabs-top");
45432     }
45433     this.items = [];
45434
45435     this.bodyEl.setStyle("position", "relative");
45436
45437     this.active = null;
45438     this.activateDelegate = this.activate.createDelegate(this);
45439
45440     this.addEvents({
45441         /**
45442          * @event tabchange
45443          * Fires when the active tab changes
45444          * @param {Roo.TabPanel} this
45445          * @param {Roo.TabPanelItem} activePanel The new active tab
45446          */
45447         "tabchange": true,
45448         /**
45449          * @event beforetabchange
45450          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45451          * @param {Roo.TabPanel} this
45452          * @param {Object} e Set cancel to true on this object to cancel the tab change
45453          * @param {Roo.TabPanelItem} tab The tab being changed to
45454          */
45455         "beforetabchange" : true
45456     });
45457
45458     Roo.EventManager.onWindowResize(this.onResize, this);
45459     this.cpad = this.el.getPadding("lr");
45460     this.hiddenCount = 0;
45461
45462
45463     // toolbar on the tabbar support...
45464     if (this.toolbar) {
45465         alert("no toolbar support yet");
45466         this.toolbar  = false;
45467         /*
45468         var tcfg = this.toolbar;
45469         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
45470         this.toolbar = new Roo.Toolbar(tcfg);
45471         if (Roo.isSafari) {
45472             var tbl = tcfg.container.child('table', true);
45473             tbl.setAttribute('width', '100%');
45474         }
45475         */
45476         
45477     }
45478    
45479
45480
45481     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45482 };
45483
45484 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45485     /*
45486      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45487      */
45488     tabPosition : "top",
45489     /*
45490      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45491      */
45492     currentTabWidth : 0,
45493     /*
45494      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45495      */
45496     minTabWidth : 40,
45497     /*
45498      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45499      */
45500     maxTabWidth : 250,
45501     /*
45502      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45503      */
45504     preferredTabWidth : 175,
45505     /*
45506      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45507      */
45508     resizeTabs : false,
45509     /*
45510      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45511      */
45512     monitorResize : true,
45513     /*
45514      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
45515      */
45516     toolbar : false,  // set by caller..
45517     
45518     region : false, /// set by caller
45519     
45520     disableTooltips : true, // not used yet...
45521
45522     /**
45523      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45524      * @param {String} id The id of the div to use <b>or create</b>
45525      * @param {String} text The text for the tab
45526      * @param {String} content (optional) Content to put in the TabPanelItem body
45527      * @param {Boolean} closable (optional) True to create a close icon on the tab
45528      * @return {Roo.TabPanelItem} The created TabPanelItem
45529      */
45530     addTab : function(id, text, content, closable, tpl)
45531     {
45532         var item = new Roo.bootstrap.panel.TabItem({
45533             panel: this,
45534             id : id,
45535             text : text,
45536             closable : closable,
45537             tpl : tpl
45538         });
45539         this.addTabItem(item);
45540         if(content){
45541             item.setContent(content);
45542         }
45543         return item;
45544     },
45545
45546     /**
45547      * Returns the {@link Roo.TabPanelItem} with the specified id/index
45548      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45549      * @return {Roo.TabPanelItem}
45550      */
45551     getTab : function(id){
45552         return this.items[id];
45553     },
45554
45555     /**
45556      * Hides the {@link Roo.TabPanelItem} with the specified id/index
45557      * @param {String/Number} id The id or index of the TabPanelItem to hide.
45558      */
45559     hideTab : function(id){
45560         var t = this.items[id];
45561         if(!t.isHidden()){
45562            t.setHidden(true);
45563            this.hiddenCount++;
45564            this.autoSizeTabs();
45565         }
45566     },
45567
45568     /**
45569      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
45570      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
45571      */
45572     unhideTab : function(id){
45573         var t = this.items[id];
45574         if(t.isHidden()){
45575            t.setHidden(false);
45576            this.hiddenCount--;
45577            this.autoSizeTabs();
45578         }
45579     },
45580
45581     /**
45582      * Adds an existing {@link Roo.TabPanelItem}.
45583      * @param {Roo.TabPanelItem} item The TabPanelItem to add
45584      */
45585     addTabItem : function(item)
45586     {
45587         this.items[item.id] = item;
45588         this.items.push(item);
45589         this.autoSizeTabs();
45590       //  if(this.resizeTabs){
45591     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
45592   //         this.autoSizeTabs();
45593 //        }else{
45594 //            item.autoSize();
45595        // }
45596     },
45597
45598     /**
45599      * Removes a {@link Roo.TabPanelItem}.
45600      * @param {String/Number} id The id or index of the TabPanelItem to remove.
45601      */
45602     removeTab : function(id){
45603         var items = this.items;
45604         var tab = items[id];
45605         if(!tab) { return; }
45606         var index = items.indexOf(tab);
45607         if(this.active == tab && items.length > 1){
45608             var newTab = this.getNextAvailable(index);
45609             if(newTab) {
45610                 newTab.activate();
45611             }
45612         }
45613         this.stripEl.dom.removeChild(tab.pnode.dom);
45614         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
45615             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
45616         }
45617         items.splice(index, 1);
45618         delete this.items[tab.id];
45619         tab.fireEvent("close", tab);
45620         tab.purgeListeners();
45621         this.autoSizeTabs();
45622     },
45623
45624     getNextAvailable : function(start){
45625         var items = this.items;
45626         var index = start;
45627         // look for a next tab that will slide over to
45628         // replace the one being removed
45629         while(index < items.length){
45630             var item = items[++index];
45631             if(item && !item.isHidden()){
45632                 return item;
45633             }
45634         }
45635         // if one isn't found select the previous tab (on the left)
45636         index = start;
45637         while(index >= 0){
45638             var item = items[--index];
45639             if(item && !item.isHidden()){
45640                 return item;
45641             }
45642         }
45643         return null;
45644     },
45645
45646     /**
45647      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
45648      * @param {String/Number} id The id or index of the TabPanelItem to disable.
45649      */
45650     disableTab : function(id){
45651         var tab = this.items[id];
45652         if(tab && this.active != tab){
45653             tab.disable();
45654         }
45655     },
45656
45657     /**
45658      * Enables a {@link Roo.TabPanelItem} that is disabled.
45659      * @param {String/Number} id The id or index of the TabPanelItem to enable.
45660      */
45661     enableTab : function(id){
45662         var tab = this.items[id];
45663         tab.enable();
45664     },
45665
45666     /**
45667      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
45668      * @param {String/Number} id The id or index of the TabPanelItem to activate.
45669      * @return {Roo.TabPanelItem} The TabPanelItem.
45670      */
45671     activate : function(id)
45672     {
45673         //Roo.log('activite:'  + id);
45674         
45675         var tab = this.items[id];
45676         if(!tab){
45677             return null;
45678         }
45679         if(tab == this.active || tab.disabled){
45680             return tab;
45681         }
45682         var e = {};
45683         this.fireEvent("beforetabchange", this, e, tab);
45684         if(e.cancel !== true && !tab.disabled){
45685             if(this.active){
45686                 this.active.hide();
45687             }
45688             this.active = this.items[id];
45689             this.active.show();
45690             this.fireEvent("tabchange", this, this.active);
45691         }
45692         return tab;
45693     },
45694
45695     /**
45696      * Gets the active {@link Roo.TabPanelItem}.
45697      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
45698      */
45699     getActiveTab : function(){
45700         return this.active;
45701     },
45702
45703     /**
45704      * Updates the tab body element to fit the height of the container element
45705      * for overflow scrolling
45706      * @param {Number} targetHeight (optional) Override the starting height from the elements height
45707      */
45708     syncHeight : function(targetHeight){
45709         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45710         var bm = this.bodyEl.getMargins();
45711         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
45712         this.bodyEl.setHeight(newHeight);
45713         return newHeight;
45714     },
45715
45716     onResize : function(){
45717         if(this.monitorResize){
45718             this.autoSizeTabs();
45719         }
45720     },
45721
45722     /**
45723      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
45724      */
45725     beginUpdate : function(){
45726         this.updating = true;
45727     },
45728
45729     /**
45730      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
45731      */
45732     endUpdate : function(){
45733         this.updating = false;
45734         this.autoSizeTabs();
45735     },
45736
45737     /**
45738      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
45739      */
45740     autoSizeTabs : function()
45741     {
45742         var count = this.items.length;
45743         var vcount = count - this.hiddenCount;
45744         
45745         if (vcount < 2) {
45746             this.stripEl.hide();
45747         } else {
45748             this.stripEl.show();
45749         }
45750         
45751         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
45752             return;
45753         }
45754         
45755         
45756         var w = Math.max(this.el.getWidth() - this.cpad, 10);
45757         var availWidth = Math.floor(w / vcount);
45758         var b = this.stripBody;
45759         if(b.getWidth() > w){
45760             var tabs = this.items;
45761             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
45762             if(availWidth < this.minTabWidth){
45763                 /*if(!this.sleft){    // incomplete scrolling code
45764                     this.createScrollButtons();
45765                 }
45766                 this.showScroll();
45767                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
45768             }
45769         }else{
45770             if(this.currentTabWidth < this.preferredTabWidth){
45771                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
45772             }
45773         }
45774     },
45775
45776     /**
45777      * Returns the number of tabs in this TabPanel.
45778      * @return {Number}
45779      */
45780      getCount : function(){
45781          return this.items.length;
45782      },
45783
45784     /**
45785      * Resizes all the tabs to the passed width
45786      * @param {Number} The new width
45787      */
45788     setTabWidth : function(width){
45789         this.currentTabWidth = width;
45790         for(var i = 0, len = this.items.length; i < len; i++) {
45791                 if(!this.items[i].isHidden()) {
45792                 this.items[i].setWidth(width);
45793             }
45794         }
45795     },
45796
45797     /**
45798      * Destroys this TabPanel
45799      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
45800      */
45801     destroy : function(removeEl){
45802         Roo.EventManager.removeResizeListener(this.onResize, this);
45803         for(var i = 0, len = this.items.length; i < len; i++){
45804             this.items[i].purgeListeners();
45805         }
45806         if(removeEl === true){
45807             this.el.update("");
45808             this.el.remove();
45809         }
45810     },
45811     
45812     createStrip : function(container)
45813     {
45814         var strip = document.createElement("nav");
45815         strip.className = Roo.bootstrap.version == 4 ?
45816             "navbar-light bg-light" : 
45817             "navbar navbar-default"; //"x-tabs-wrap";
45818         container.appendChild(strip);
45819         return strip;
45820     },
45821     
45822     createStripList : function(strip)
45823     {
45824         // div wrapper for retard IE
45825         // returns the "tr" element.
45826         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
45827         //'<div class="x-tabs-strip-wrap">'+
45828           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
45829           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
45830         return strip.firstChild; //.firstChild.firstChild.firstChild;
45831     },
45832     createBody : function(container)
45833     {
45834         var body = document.createElement("div");
45835         Roo.id(body, "tab-body");
45836         //Roo.fly(body).addClass("x-tabs-body");
45837         Roo.fly(body).addClass("tab-content");
45838         container.appendChild(body);
45839         return body;
45840     },
45841     createItemBody :function(bodyEl, id){
45842         var body = Roo.getDom(id);
45843         if(!body){
45844             body = document.createElement("div");
45845             body.id = id;
45846         }
45847         //Roo.fly(body).addClass("x-tabs-item-body");
45848         Roo.fly(body).addClass("tab-pane");
45849          bodyEl.insertBefore(body, bodyEl.firstChild);
45850         return body;
45851     },
45852     /** @private */
45853     createStripElements :  function(stripEl, text, closable, tpl)
45854     {
45855         var td = document.createElement("li"); // was td..
45856         td.className = 'nav-item';
45857         
45858         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
45859         
45860         
45861         stripEl.appendChild(td);
45862         /*if(closable){
45863             td.className = "x-tabs-closable";
45864             if(!this.closeTpl){
45865                 this.closeTpl = new Roo.Template(
45866                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45867                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
45868                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
45869                 );
45870             }
45871             var el = this.closeTpl.overwrite(td, {"text": text});
45872             var close = el.getElementsByTagName("div")[0];
45873             var inner = el.getElementsByTagName("em")[0];
45874             return {"el": el, "close": close, "inner": inner};
45875         } else {
45876         */
45877         // not sure what this is..
45878 //            if(!this.tabTpl){
45879                 //this.tabTpl = new Roo.Template(
45880                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45881                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
45882                 //);
45883 //                this.tabTpl = new Roo.Template(
45884 //                   '<a href="#">' +
45885 //                   '<span unselectable="on"' +
45886 //                            (this.disableTooltips ? '' : ' title="{text}"') +
45887 //                            ' >{text}</span></a>'
45888 //                );
45889 //                
45890 //            }
45891
45892
45893             var template = tpl || this.tabTpl || false;
45894             
45895             if(!template){
45896                 template =  new Roo.Template(
45897                         Roo.bootstrap.version == 4 ? 
45898                             (
45899                                 '<a class="nav-link" href="#" unselectable="on"' +
45900                                      (this.disableTooltips ? '' : ' title="{text}"') +
45901                                      ' >{text}</a>'
45902                             ) : (
45903                                 '<a class="nav-link" href="#">' +
45904                                 '<span unselectable="on"' +
45905                                          (this.disableTooltips ? '' : ' title="{text}"') +
45906                                     ' >{text}</span></a>'
45907                             )
45908                 );
45909             }
45910             
45911             switch (typeof(template)) {
45912                 case 'object' :
45913                     break;
45914                 case 'string' :
45915                     template = new Roo.Template(template);
45916                     break;
45917                 default :
45918                     break;
45919             }
45920             
45921             var el = template.overwrite(td, {"text": text});
45922             
45923             var inner = el.getElementsByTagName("span")[0];
45924             
45925             return {"el": el, "inner": inner};
45926             
45927     }
45928         
45929     
45930 });
45931
45932 /**
45933  * @class Roo.TabPanelItem
45934  * @extends Roo.util.Observable
45935  * Represents an individual item (tab plus body) in a TabPanel.
45936  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
45937  * @param {String} id The id of this TabPanelItem
45938  * @param {String} text The text for the tab of this TabPanelItem
45939  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
45940  */
45941 Roo.bootstrap.panel.TabItem = function(config){
45942     /**
45943      * The {@link Roo.TabPanel} this TabPanelItem belongs to
45944      * @type Roo.TabPanel
45945      */
45946     this.tabPanel = config.panel;
45947     /**
45948      * The id for this TabPanelItem
45949      * @type String
45950      */
45951     this.id = config.id;
45952     /** @private */
45953     this.disabled = false;
45954     /** @private */
45955     this.text = config.text;
45956     /** @private */
45957     this.loaded = false;
45958     this.closable = config.closable;
45959
45960     /**
45961      * The body element for this TabPanelItem.
45962      * @type Roo.Element
45963      */
45964     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
45965     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
45966     this.bodyEl.setStyle("display", "block");
45967     this.bodyEl.setStyle("zoom", "1");
45968     //this.hideAction();
45969
45970     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
45971     /** @private */
45972     this.el = Roo.get(els.el);
45973     this.inner = Roo.get(els.inner, true);
45974      this.textEl = Roo.bootstrap.version == 4 ?
45975         this.el : Roo.get(this.el.dom.firstChild, true);
45976
45977     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
45978     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
45979
45980     
45981 //    this.el.on("mousedown", this.onTabMouseDown, this);
45982     this.el.on("click", this.onTabClick, this);
45983     /** @private */
45984     if(config.closable){
45985         var c = Roo.get(els.close, true);
45986         c.dom.title = this.closeText;
45987         c.addClassOnOver("close-over");
45988         c.on("click", this.closeClick, this);
45989      }
45990
45991     this.addEvents({
45992          /**
45993          * @event activate
45994          * Fires when this tab becomes the active tab.
45995          * @param {Roo.TabPanel} tabPanel The parent TabPanel
45996          * @param {Roo.TabPanelItem} this
45997          */
45998         "activate": true,
45999         /**
46000          * @event beforeclose
46001          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46002          * @param {Roo.TabPanelItem} this
46003          * @param {Object} e Set cancel to true on this object to cancel the close.
46004          */
46005         "beforeclose": true,
46006         /**
46007          * @event close
46008          * Fires when this tab is closed.
46009          * @param {Roo.TabPanelItem} this
46010          */
46011          "close": true,
46012         /**
46013          * @event deactivate
46014          * Fires when this tab is no longer the active tab.
46015          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46016          * @param {Roo.TabPanelItem} this
46017          */
46018          "deactivate" : true
46019     });
46020     this.hidden = false;
46021
46022     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46023 };
46024
46025 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46026            {
46027     purgeListeners : function(){
46028        Roo.util.Observable.prototype.purgeListeners.call(this);
46029        this.el.removeAllListeners();
46030     },
46031     /**
46032      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46033      */
46034     show : function(){
46035         this.status_node.addClass("active");
46036         this.showAction();
46037         if(Roo.isOpera){
46038             this.tabPanel.stripWrap.repaint();
46039         }
46040         this.fireEvent("activate", this.tabPanel, this);
46041     },
46042
46043     /**
46044      * Returns true if this tab is the active tab.
46045      * @return {Boolean}
46046      */
46047     isActive : function(){
46048         return this.tabPanel.getActiveTab() == this;
46049     },
46050
46051     /**
46052      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46053      */
46054     hide : function(){
46055         this.status_node.removeClass("active");
46056         this.hideAction();
46057         this.fireEvent("deactivate", this.tabPanel, this);
46058     },
46059
46060     hideAction : function(){
46061         this.bodyEl.hide();
46062         this.bodyEl.setStyle("position", "absolute");
46063         this.bodyEl.setLeft("-20000px");
46064         this.bodyEl.setTop("-20000px");
46065     },
46066
46067     showAction : function(){
46068         this.bodyEl.setStyle("position", "relative");
46069         this.bodyEl.setTop("");
46070         this.bodyEl.setLeft("");
46071         this.bodyEl.show();
46072     },
46073
46074     /**
46075      * Set the tooltip for the tab.
46076      * @param {String} tooltip The tab's tooltip
46077      */
46078     setTooltip : function(text){
46079         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46080             this.textEl.dom.qtip = text;
46081             this.textEl.dom.removeAttribute('title');
46082         }else{
46083             this.textEl.dom.title = text;
46084         }
46085     },
46086
46087     onTabClick : function(e){
46088         e.preventDefault();
46089         this.tabPanel.activate(this.id);
46090     },
46091
46092     onTabMouseDown : function(e){
46093         e.preventDefault();
46094         this.tabPanel.activate(this.id);
46095     },
46096 /*
46097     getWidth : function(){
46098         return this.inner.getWidth();
46099     },
46100
46101     setWidth : function(width){
46102         var iwidth = width - this.linode.getPadding("lr");
46103         this.inner.setWidth(iwidth);
46104         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46105         this.linode.setWidth(width);
46106     },
46107 */
46108     /**
46109      * Show or hide the tab
46110      * @param {Boolean} hidden True to hide or false to show.
46111      */
46112     setHidden : function(hidden){
46113         this.hidden = hidden;
46114         this.linode.setStyle("display", hidden ? "none" : "");
46115     },
46116
46117     /**
46118      * Returns true if this tab is "hidden"
46119      * @return {Boolean}
46120      */
46121     isHidden : function(){
46122         return this.hidden;
46123     },
46124
46125     /**
46126      * Returns the text for this tab
46127      * @return {String}
46128      */
46129     getText : function(){
46130         return this.text;
46131     },
46132     /*
46133     autoSize : function(){
46134         //this.el.beginMeasure();
46135         this.textEl.setWidth(1);
46136         /*
46137          *  #2804 [new] Tabs in Roojs
46138          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46139          */
46140         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46141         //this.el.endMeasure();
46142     //},
46143
46144     /**
46145      * Sets the text for the tab (Note: this also sets the tooltip text)
46146      * @param {String} text The tab's text and tooltip
46147      */
46148     setText : function(text){
46149         this.text = text;
46150         this.textEl.update(text);
46151         this.setTooltip(text);
46152         //if(!this.tabPanel.resizeTabs){
46153         //    this.autoSize();
46154         //}
46155     },
46156     /**
46157      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46158      */
46159     activate : function(){
46160         this.tabPanel.activate(this.id);
46161     },
46162
46163     /**
46164      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46165      */
46166     disable : function(){
46167         if(this.tabPanel.active != this){
46168             this.disabled = true;
46169             this.status_node.addClass("disabled");
46170         }
46171     },
46172
46173     /**
46174      * Enables this TabPanelItem if it was previously disabled.
46175      */
46176     enable : function(){
46177         this.disabled = false;
46178         this.status_node.removeClass("disabled");
46179     },
46180
46181     /**
46182      * Sets the content for this TabPanelItem.
46183      * @param {String} content The content
46184      * @param {Boolean} loadScripts true to look for and load scripts
46185      */
46186     setContent : function(content, loadScripts){
46187         this.bodyEl.update(content, loadScripts);
46188     },
46189
46190     /**
46191      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46192      * @return {Roo.UpdateManager} The UpdateManager
46193      */
46194     getUpdateManager : function(){
46195         return this.bodyEl.getUpdateManager();
46196     },
46197
46198     /**
46199      * Set a URL to be used to load the content for this TabPanelItem.
46200      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46201      * @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)
46202      * @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)
46203      * @return {Roo.UpdateManager} The UpdateManager
46204      */
46205     setUrl : function(url, params, loadOnce){
46206         if(this.refreshDelegate){
46207             this.un('activate', this.refreshDelegate);
46208         }
46209         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46210         this.on("activate", this.refreshDelegate);
46211         return this.bodyEl.getUpdateManager();
46212     },
46213
46214     /** @private */
46215     _handleRefresh : function(url, params, loadOnce){
46216         if(!loadOnce || !this.loaded){
46217             var updater = this.bodyEl.getUpdateManager();
46218             updater.update(url, params, this._setLoaded.createDelegate(this));
46219         }
46220     },
46221
46222     /**
46223      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46224      *   Will fail silently if the setUrl method has not been called.
46225      *   This does not activate the panel, just updates its content.
46226      */
46227     refresh : function(){
46228         if(this.refreshDelegate){
46229            this.loaded = false;
46230            this.refreshDelegate();
46231         }
46232     },
46233
46234     /** @private */
46235     _setLoaded : function(){
46236         this.loaded = true;
46237     },
46238
46239     /** @private */
46240     closeClick : function(e){
46241         var o = {};
46242         e.stopEvent();
46243         this.fireEvent("beforeclose", this, o);
46244         if(o.cancel !== true){
46245             this.tabPanel.removeTab(this.id);
46246         }
46247     },
46248     /**
46249      * The text displayed in the tooltip for the close icon.
46250      * @type String
46251      */
46252     closeText : "Close this tab"
46253 });
46254 /**
46255 *    This script refer to:
46256 *    Title: International Telephone Input
46257 *    Author: Jack O'Connor
46258 *    Code version:  v12.1.12
46259 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46260 **/
46261
46262 Roo.bootstrap.form.PhoneInputData = function() {
46263     var d = [
46264       [
46265         "Afghanistan (‫افغانستان‬‎)",
46266         "af",
46267         "93"
46268       ],
46269       [
46270         "Albania (Shqipëri)",
46271         "al",
46272         "355"
46273       ],
46274       [
46275         "Algeria (‫الجزائر‬‎)",
46276         "dz",
46277         "213"
46278       ],
46279       [
46280         "American Samoa",
46281         "as",
46282         "1684"
46283       ],
46284       [
46285         "Andorra",
46286         "ad",
46287         "376"
46288       ],
46289       [
46290         "Angola",
46291         "ao",
46292         "244"
46293       ],
46294       [
46295         "Anguilla",
46296         "ai",
46297         "1264"
46298       ],
46299       [
46300         "Antigua and Barbuda",
46301         "ag",
46302         "1268"
46303       ],
46304       [
46305         "Argentina",
46306         "ar",
46307         "54"
46308       ],
46309       [
46310         "Armenia (Հայաստան)",
46311         "am",
46312         "374"
46313       ],
46314       [
46315         "Aruba",
46316         "aw",
46317         "297"
46318       ],
46319       [
46320         "Australia",
46321         "au",
46322         "61",
46323         0
46324       ],
46325       [
46326         "Austria (Österreich)",
46327         "at",
46328         "43"
46329       ],
46330       [
46331         "Azerbaijan (Azərbaycan)",
46332         "az",
46333         "994"
46334       ],
46335       [
46336         "Bahamas",
46337         "bs",
46338         "1242"
46339       ],
46340       [
46341         "Bahrain (‫البحرين‬‎)",
46342         "bh",
46343         "973"
46344       ],
46345       [
46346         "Bangladesh (বাংলাদেশ)",
46347         "bd",
46348         "880"
46349       ],
46350       [
46351         "Barbados",
46352         "bb",
46353         "1246"
46354       ],
46355       [
46356         "Belarus (Беларусь)",
46357         "by",
46358         "375"
46359       ],
46360       [
46361         "Belgium (België)",
46362         "be",
46363         "32"
46364       ],
46365       [
46366         "Belize",
46367         "bz",
46368         "501"
46369       ],
46370       [
46371         "Benin (Bénin)",
46372         "bj",
46373         "229"
46374       ],
46375       [
46376         "Bermuda",
46377         "bm",
46378         "1441"
46379       ],
46380       [
46381         "Bhutan (འབྲུག)",
46382         "bt",
46383         "975"
46384       ],
46385       [
46386         "Bolivia",
46387         "bo",
46388         "591"
46389       ],
46390       [
46391         "Bosnia and Herzegovina (Босна и Херцеговина)",
46392         "ba",
46393         "387"
46394       ],
46395       [
46396         "Botswana",
46397         "bw",
46398         "267"
46399       ],
46400       [
46401         "Brazil (Brasil)",
46402         "br",
46403         "55"
46404       ],
46405       [
46406         "British Indian Ocean Territory",
46407         "io",
46408         "246"
46409       ],
46410       [
46411         "British Virgin Islands",
46412         "vg",
46413         "1284"
46414       ],
46415       [
46416         "Brunei",
46417         "bn",
46418         "673"
46419       ],
46420       [
46421         "Bulgaria (България)",
46422         "bg",
46423         "359"
46424       ],
46425       [
46426         "Burkina Faso",
46427         "bf",
46428         "226"
46429       ],
46430       [
46431         "Burundi (Uburundi)",
46432         "bi",
46433         "257"
46434       ],
46435       [
46436         "Cambodia (កម្ពុជា)",
46437         "kh",
46438         "855"
46439       ],
46440       [
46441         "Cameroon (Cameroun)",
46442         "cm",
46443         "237"
46444       ],
46445       [
46446         "Canada",
46447         "ca",
46448         "1",
46449         1,
46450         ["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"]
46451       ],
46452       [
46453         "Cape Verde (Kabu Verdi)",
46454         "cv",
46455         "238"
46456       ],
46457       [
46458         "Caribbean Netherlands",
46459         "bq",
46460         "599",
46461         1
46462       ],
46463       [
46464         "Cayman Islands",
46465         "ky",
46466         "1345"
46467       ],
46468       [
46469         "Central African Republic (République centrafricaine)",
46470         "cf",
46471         "236"
46472       ],
46473       [
46474         "Chad (Tchad)",
46475         "td",
46476         "235"
46477       ],
46478       [
46479         "Chile",
46480         "cl",
46481         "56"
46482       ],
46483       [
46484         "China (中国)",
46485         "cn",
46486         "86"
46487       ],
46488       [
46489         "Christmas Island",
46490         "cx",
46491         "61",
46492         2
46493       ],
46494       [
46495         "Cocos (Keeling) Islands",
46496         "cc",
46497         "61",
46498         1
46499       ],
46500       [
46501         "Colombia",
46502         "co",
46503         "57"
46504       ],
46505       [
46506         "Comoros (‫جزر القمر‬‎)",
46507         "km",
46508         "269"
46509       ],
46510       [
46511         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46512         "cd",
46513         "243"
46514       ],
46515       [
46516         "Congo (Republic) (Congo-Brazzaville)",
46517         "cg",
46518         "242"
46519       ],
46520       [
46521         "Cook Islands",
46522         "ck",
46523         "682"
46524       ],
46525       [
46526         "Costa Rica",
46527         "cr",
46528         "506"
46529       ],
46530       [
46531         "Côte d’Ivoire",
46532         "ci",
46533         "225"
46534       ],
46535       [
46536         "Croatia (Hrvatska)",
46537         "hr",
46538         "385"
46539       ],
46540       [
46541         "Cuba",
46542         "cu",
46543         "53"
46544       ],
46545       [
46546         "Curaçao",
46547         "cw",
46548         "599",
46549         0
46550       ],
46551       [
46552         "Cyprus (Κύπρος)",
46553         "cy",
46554         "357"
46555       ],
46556       [
46557         "Czech Republic (Česká republika)",
46558         "cz",
46559         "420"
46560       ],
46561       [
46562         "Denmark (Danmark)",
46563         "dk",
46564         "45"
46565       ],
46566       [
46567         "Djibouti",
46568         "dj",
46569         "253"
46570       ],
46571       [
46572         "Dominica",
46573         "dm",
46574         "1767"
46575       ],
46576       [
46577         "Dominican Republic (República Dominicana)",
46578         "do",
46579         "1",
46580         2,
46581         ["809", "829", "849"]
46582       ],
46583       [
46584         "Ecuador",
46585         "ec",
46586         "593"
46587       ],
46588       [
46589         "Egypt (‫مصر‬‎)",
46590         "eg",
46591         "20"
46592       ],
46593       [
46594         "El Salvador",
46595         "sv",
46596         "503"
46597       ],
46598       [
46599         "Equatorial Guinea (Guinea Ecuatorial)",
46600         "gq",
46601         "240"
46602       ],
46603       [
46604         "Eritrea",
46605         "er",
46606         "291"
46607       ],
46608       [
46609         "Estonia (Eesti)",
46610         "ee",
46611         "372"
46612       ],
46613       [
46614         "Ethiopia",
46615         "et",
46616         "251"
46617       ],
46618       [
46619         "Falkland Islands (Islas Malvinas)",
46620         "fk",
46621         "500"
46622       ],
46623       [
46624         "Faroe Islands (Føroyar)",
46625         "fo",
46626         "298"
46627       ],
46628       [
46629         "Fiji",
46630         "fj",
46631         "679"
46632       ],
46633       [
46634         "Finland (Suomi)",
46635         "fi",
46636         "358",
46637         0
46638       ],
46639       [
46640         "France",
46641         "fr",
46642         "33"
46643       ],
46644       [
46645         "French Guiana (Guyane française)",
46646         "gf",
46647         "594"
46648       ],
46649       [
46650         "French Polynesia (Polynésie française)",
46651         "pf",
46652         "689"
46653       ],
46654       [
46655         "Gabon",
46656         "ga",
46657         "241"
46658       ],
46659       [
46660         "Gambia",
46661         "gm",
46662         "220"
46663       ],
46664       [
46665         "Georgia (საქართველო)",
46666         "ge",
46667         "995"
46668       ],
46669       [
46670         "Germany (Deutschland)",
46671         "de",
46672         "49"
46673       ],
46674       [
46675         "Ghana (Gaana)",
46676         "gh",
46677         "233"
46678       ],
46679       [
46680         "Gibraltar",
46681         "gi",
46682         "350"
46683       ],
46684       [
46685         "Greece (Ελλάδα)",
46686         "gr",
46687         "30"
46688       ],
46689       [
46690         "Greenland (Kalaallit Nunaat)",
46691         "gl",
46692         "299"
46693       ],
46694       [
46695         "Grenada",
46696         "gd",
46697         "1473"
46698       ],
46699       [
46700         "Guadeloupe",
46701         "gp",
46702         "590",
46703         0
46704       ],
46705       [
46706         "Guam",
46707         "gu",
46708         "1671"
46709       ],
46710       [
46711         "Guatemala",
46712         "gt",
46713         "502"
46714       ],
46715       [
46716         "Guernsey",
46717         "gg",
46718         "44",
46719         1
46720       ],
46721       [
46722         "Guinea (Guinée)",
46723         "gn",
46724         "224"
46725       ],
46726       [
46727         "Guinea-Bissau (Guiné Bissau)",
46728         "gw",
46729         "245"
46730       ],
46731       [
46732         "Guyana",
46733         "gy",
46734         "592"
46735       ],
46736       [
46737         "Haiti",
46738         "ht",
46739         "509"
46740       ],
46741       [
46742         "Honduras",
46743         "hn",
46744         "504"
46745       ],
46746       [
46747         "Hong Kong (香港)",
46748         "hk",
46749         "852"
46750       ],
46751       [
46752         "Hungary (Magyarország)",
46753         "hu",
46754         "36"
46755       ],
46756       [
46757         "Iceland (Ísland)",
46758         "is",
46759         "354"
46760       ],
46761       [
46762         "India (भारत)",
46763         "in",
46764         "91"
46765       ],
46766       [
46767         "Indonesia",
46768         "id",
46769         "62"
46770       ],
46771       [
46772         "Iran (‫ایران‬‎)",
46773         "ir",
46774         "98"
46775       ],
46776       [
46777         "Iraq (‫العراق‬‎)",
46778         "iq",
46779         "964"
46780       ],
46781       [
46782         "Ireland",
46783         "ie",
46784         "353"
46785       ],
46786       [
46787         "Isle of Man",
46788         "im",
46789         "44",
46790         2
46791       ],
46792       [
46793         "Israel (‫ישראל‬‎)",
46794         "il",
46795         "972"
46796       ],
46797       [
46798         "Italy (Italia)",
46799         "it",
46800         "39",
46801         0
46802       ],
46803       [
46804         "Jamaica",
46805         "jm",
46806         "1876"
46807       ],
46808       [
46809         "Japan (日本)",
46810         "jp",
46811         "81"
46812       ],
46813       [
46814         "Jersey",
46815         "je",
46816         "44",
46817         3
46818       ],
46819       [
46820         "Jordan (‫الأردن‬‎)",
46821         "jo",
46822         "962"
46823       ],
46824       [
46825         "Kazakhstan (Казахстан)",
46826         "kz",
46827         "7",
46828         1
46829       ],
46830       [
46831         "Kenya",
46832         "ke",
46833         "254"
46834       ],
46835       [
46836         "Kiribati",
46837         "ki",
46838         "686"
46839       ],
46840       [
46841         "Kosovo",
46842         "xk",
46843         "383"
46844       ],
46845       [
46846         "Kuwait (‫الكويت‬‎)",
46847         "kw",
46848         "965"
46849       ],
46850       [
46851         "Kyrgyzstan (Кыргызстан)",
46852         "kg",
46853         "996"
46854       ],
46855       [
46856         "Laos (ລາວ)",
46857         "la",
46858         "856"
46859       ],
46860       [
46861         "Latvia (Latvija)",
46862         "lv",
46863         "371"
46864       ],
46865       [
46866         "Lebanon (‫لبنان‬‎)",
46867         "lb",
46868         "961"
46869       ],
46870       [
46871         "Lesotho",
46872         "ls",
46873         "266"
46874       ],
46875       [
46876         "Liberia",
46877         "lr",
46878         "231"
46879       ],
46880       [
46881         "Libya (‫ليبيا‬‎)",
46882         "ly",
46883         "218"
46884       ],
46885       [
46886         "Liechtenstein",
46887         "li",
46888         "423"
46889       ],
46890       [
46891         "Lithuania (Lietuva)",
46892         "lt",
46893         "370"
46894       ],
46895       [
46896         "Luxembourg",
46897         "lu",
46898         "352"
46899       ],
46900       [
46901         "Macau (澳門)",
46902         "mo",
46903         "853"
46904       ],
46905       [
46906         "Macedonia (FYROM) (Македонија)",
46907         "mk",
46908         "389"
46909       ],
46910       [
46911         "Madagascar (Madagasikara)",
46912         "mg",
46913         "261"
46914       ],
46915       [
46916         "Malawi",
46917         "mw",
46918         "265"
46919       ],
46920       [
46921         "Malaysia",
46922         "my",
46923         "60"
46924       ],
46925       [
46926         "Maldives",
46927         "mv",
46928         "960"
46929       ],
46930       [
46931         "Mali",
46932         "ml",
46933         "223"
46934       ],
46935       [
46936         "Malta",
46937         "mt",
46938         "356"
46939       ],
46940       [
46941         "Marshall Islands",
46942         "mh",
46943         "692"
46944       ],
46945       [
46946         "Martinique",
46947         "mq",
46948         "596"
46949       ],
46950       [
46951         "Mauritania (‫موريتانيا‬‎)",
46952         "mr",
46953         "222"
46954       ],
46955       [
46956         "Mauritius (Moris)",
46957         "mu",
46958         "230"
46959       ],
46960       [
46961         "Mayotte",
46962         "yt",
46963         "262",
46964         1
46965       ],
46966       [
46967         "Mexico (México)",
46968         "mx",
46969         "52"
46970       ],
46971       [
46972         "Micronesia",
46973         "fm",
46974         "691"
46975       ],
46976       [
46977         "Moldova (Republica Moldova)",
46978         "md",
46979         "373"
46980       ],
46981       [
46982         "Monaco",
46983         "mc",
46984         "377"
46985       ],
46986       [
46987         "Mongolia (Монгол)",
46988         "mn",
46989         "976"
46990       ],
46991       [
46992         "Montenegro (Crna Gora)",
46993         "me",
46994         "382"
46995       ],
46996       [
46997         "Montserrat",
46998         "ms",
46999         "1664"
47000       ],
47001       [
47002         "Morocco (‫المغرب‬‎)",
47003         "ma",
47004         "212",
47005         0
47006       ],
47007       [
47008         "Mozambique (Moçambique)",
47009         "mz",
47010         "258"
47011       ],
47012       [
47013         "Myanmar (Burma) (မြန်မာ)",
47014         "mm",
47015         "95"
47016       ],
47017       [
47018         "Namibia (Namibië)",
47019         "na",
47020         "264"
47021       ],
47022       [
47023         "Nauru",
47024         "nr",
47025         "674"
47026       ],
47027       [
47028         "Nepal (नेपाल)",
47029         "np",
47030         "977"
47031       ],
47032       [
47033         "Netherlands (Nederland)",
47034         "nl",
47035         "31"
47036       ],
47037       [
47038         "New Caledonia (Nouvelle-Calédonie)",
47039         "nc",
47040         "687"
47041       ],
47042       [
47043         "New Zealand",
47044         "nz",
47045         "64"
47046       ],
47047       [
47048         "Nicaragua",
47049         "ni",
47050         "505"
47051       ],
47052       [
47053         "Niger (Nijar)",
47054         "ne",
47055         "227"
47056       ],
47057       [
47058         "Nigeria",
47059         "ng",
47060         "234"
47061       ],
47062       [
47063         "Niue",
47064         "nu",
47065         "683"
47066       ],
47067       [
47068         "Norfolk Island",
47069         "nf",
47070         "672"
47071       ],
47072       [
47073         "North Korea (조선 민주주의 인민 공화국)",
47074         "kp",
47075         "850"
47076       ],
47077       [
47078         "Northern Mariana Islands",
47079         "mp",
47080         "1670"
47081       ],
47082       [
47083         "Norway (Norge)",
47084         "no",
47085         "47",
47086         0
47087       ],
47088       [
47089         "Oman (‫عُمان‬‎)",
47090         "om",
47091         "968"
47092       ],
47093       [
47094         "Pakistan (‫پاکستان‬‎)",
47095         "pk",
47096         "92"
47097       ],
47098       [
47099         "Palau",
47100         "pw",
47101         "680"
47102       ],
47103       [
47104         "Palestine (‫فلسطين‬‎)",
47105         "ps",
47106         "970"
47107       ],
47108       [
47109         "Panama (Panamá)",
47110         "pa",
47111         "507"
47112       ],
47113       [
47114         "Papua New Guinea",
47115         "pg",
47116         "675"
47117       ],
47118       [
47119         "Paraguay",
47120         "py",
47121         "595"
47122       ],
47123       [
47124         "Peru (Perú)",
47125         "pe",
47126         "51"
47127       ],
47128       [
47129         "Philippines",
47130         "ph",
47131         "63"
47132       ],
47133       [
47134         "Poland (Polska)",
47135         "pl",
47136         "48"
47137       ],
47138       [
47139         "Portugal",
47140         "pt",
47141         "351"
47142       ],
47143       [
47144         "Puerto Rico",
47145         "pr",
47146         "1",
47147         3,
47148         ["787", "939"]
47149       ],
47150       [
47151         "Qatar (‫قطر‬‎)",
47152         "qa",
47153         "974"
47154       ],
47155       [
47156         "Réunion (La Réunion)",
47157         "re",
47158         "262",
47159         0
47160       ],
47161       [
47162         "Romania (România)",
47163         "ro",
47164         "40"
47165       ],
47166       [
47167         "Russia (Россия)",
47168         "ru",
47169         "7",
47170         0
47171       ],
47172       [
47173         "Rwanda",
47174         "rw",
47175         "250"
47176       ],
47177       [
47178         "Saint Barthélemy",
47179         "bl",
47180         "590",
47181         1
47182       ],
47183       [
47184         "Saint Helena",
47185         "sh",
47186         "290"
47187       ],
47188       [
47189         "Saint Kitts and Nevis",
47190         "kn",
47191         "1869"
47192       ],
47193       [
47194         "Saint Lucia",
47195         "lc",
47196         "1758"
47197       ],
47198       [
47199         "Saint Martin (Saint-Martin (partie française))",
47200         "mf",
47201         "590",
47202         2
47203       ],
47204       [
47205         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47206         "pm",
47207         "508"
47208       ],
47209       [
47210         "Saint Vincent and the Grenadines",
47211         "vc",
47212         "1784"
47213       ],
47214       [
47215         "Samoa",
47216         "ws",
47217         "685"
47218       ],
47219       [
47220         "San Marino",
47221         "sm",
47222         "378"
47223       ],
47224       [
47225         "São Tomé and Príncipe (São Tomé e Príncipe)",
47226         "st",
47227         "239"
47228       ],
47229       [
47230         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47231         "sa",
47232         "966"
47233       ],
47234       [
47235         "Senegal (Sénégal)",
47236         "sn",
47237         "221"
47238       ],
47239       [
47240         "Serbia (Србија)",
47241         "rs",
47242         "381"
47243       ],
47244       [
47245         "Seychelles",
47246         "sc",
47247         "248"
47248       ],
47249       [
47250         "Sierra Leone",
47251         "sl",
47252         "232"
47253       ],
47254       [
47255         "Singapore",
47256         "sg",
47257         "65"
47258       ],
47259       [
47260         "Sint Maarten",
47261         "sx",
47262         "1721"
47263       ],
47264       [
47265         "Slovakia (Slovensko)",
47266         "sk",
47267         "421"
47268       ],
47269       [
47270         "Slovenia (Slovenija)",
47271         "si",
47272         "386"
47273       ],
47274       [
47275         "Solomon Islands",
47276         "sb",
47277         "677"
47278       ],
47279       [
47280         "Somalia (Soomaaliya)",
47281         "so",
47282         "252"
47283       ],
47284       [
47285         "South Africa",
47286         "za",
47287         "27"
47288       ],
47289       [
47290         "South Korea (대한민국)",
47291         "kr",
47292         "82"
47293       ],
47294       [
47295         "South Sudan (‫جنوب السودان‬‎)",
47296         "ss",
47297         "211"
47298       ],
47299       [
47300         "Spain (España)",
47301         "es",
47302         "34"
47303       ],
47304       [
47305         "Sri Lanka (ශ්‍රී ලංකාව)",
47306         "lk",
47307         "94"
47308       ],
47309       [
47310         "Sudan (‫السودان‬‎)",
47311         "sd",
47312         "249"
47313       ],
47314       [
47315         "Suriname",
47316         "sr",
47317         "597"
47318       ],
47319       [
47320         "Svalbard and Jan Mayen",
47321         "sj",
47322         "47",
47323         1
47324       ],
47325       [
47326         "Swaziland",
47327         "sz",
47328         "268"
47329       ],
47330       [
47331         "Sweden (Sverige)",
47332         "se",
47333         "46"
47334       ],
47335       [
47336         "Switzerland (Schweiz)",
47337         "ch",
47338         "41"
47339       ],
47340       [
47341         "Syria (‫سوريا‬‎)",
47342         "sy",
47343         "963"
47344       ],
47345       [
47346         "Taiwan (台灣)",
47347         "tw",
47348         "886"
47349       ],
47350       [
47351         "Tajikistan",
47352         "tj",
47353         "992"
47354       ],
47355       [
47356         "Tanzania",
47357         "tz",
47358         "255"
47359       ],
47360       [
47361         "Thailand (ไทย)",
47362         "th",
47363         "66"
47364       ],
47365       [
47366         "Timor-Leste",
47367         "tl",
47368         "670"
47369       ],
47370       [
47371         "Togo",
47372         "tg",
47373         "228"
47374       ],
47375       [
47376         "Tokelau",
47377         "tk",
47378         "690"
47379       ],
47380       [
47381         "Tonga",
47382         "to",
47383         "676"
47384       ],
47385       [
47386         "Trinidad and Tobago",
47387         "tt",
47388         "1868"
47389       ],
47390       [
47391         "Tunisia (‫تونس‬‎)",
47392         "tn",
47393         "216"
47394       ],
47395       [
47396         "Turkey (Türkiye)",
47397         "tr",
47398         "90"
47399       ],
47400       [
47401         "Turkmenistan",
47402         "tm",
47403         "993"
47404       ],
47405       [
47406         "Turks and Caicos Islands",
47407         "tc",
47408         "1649"
47409       ],
47410       [
47411         "Tuvalu",
47412         "tv",
47413         "688"
47414       ],
47415       [
47416         "U.S. Virgin Islands",
47417         "vi",
47418         "1340"
47419       ],
47420       [
47421         "Uganda",
47422         "ug",
47423         "256"
47424       ],
47425       [
47426         "Ukraine (Україна)",
47427         "ua",
47428         "380"
47429       ],
47430       [
47431         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47432         "ae",
47433         "971"
47434       ],
47435       [
47436         "United Kingdom",
47437         "gb",
47438         "44",
47439         0
47440       ],
47441       [
47442         "United States",
47443         "us",
47444         "1",
47445         0
47446       ],
47447       [
47448         "Uruguay",
47449         "uy",
47450         "598"
47451       ],
47452       [
47453         "Uzbekistan (Oʻzbekiston)",
47454         "uz",
47455         "998"
47456       ],
47457       [
47458         "Vanuatu",
47459         "vu",
47460         "678"
47461       ],
47462       [
47463         "Vatican City (Città del Vaticano)",
47464         "va",
47465         "39",
47466         1
47467       ],
47468       [
47469         "Venezuela",
47470         "ve",
47471         "58"
47472       ],
47473       [
47474         "Vietnam (Việt Nam)",
47475         "vn",
47476         "84"
47477       ],
47478       [
47479         "Wallis and Futuna (Wallis-et-Futuna)",
47480         "wf",
47481         "681"
47482       ],
47483       [
47484         "Western Sahara (‫الصحراء الغربية‬‎)",
47485         "eh",
47486         "212",
47487         1
47488       ],
47489       [
47490         "Yemen (‫اليمن‬‎)",
47491         "ye",
47492         "967"
47493       ],
47494       [
47495         "Zambia",
47496         "zm",
47497         "260"
47498       ],
47499       [
47500         "Zimbabwe",
47501         "zw",
47502         "263"
47503       ],
47504       [
47505         "Åland Islands",
47506         "ax",
47507         "358",
47508         1
47509       ]
47510   ];
47511   
47512   return d;
47513 }/**
47514 *    This script refer to:
47515 *    Title: International Telephone Input
47516 *    Author: Jack O'Connor
47517 *    Code version:  v12.1.12
47518 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47519 **/
47520
47521 /**
47522  * @class Roo.bootstrap.form.PhoneInput
47523  * @extends Roo.bootstrap.form.TriggerField
47524  * An input with International dial-code selection
47525  
47526  * @cfg {String} defaultDialCode default '+852'
47527  * @cfg {Array} preferedCountries default []
47528   
47529  * @constructor
47530  * Create a new PhoneInput.
47531  * @param {Object} config Configuration options
47532  */
47533
47534 Roo.bootstrap.form.PhoneInput = function(config) {
47535     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47536 };
47537
47538 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47539         /**
47540         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47541         */
47542         listWidth: undefined,
47543         
47544         selectedClass: 'active',
47545         
47546         invalidClass : "has-warning",
47547         
47548         validClass: 'has-success',
47549         
47550         allowed: '0123456789',
47551         
47552         max_length: 15,
47553         
47554         /**
47555          * @cfg {String} defaultDialCode The default dial code when initializing the input
47556          */
47557         defaultDialCode: '+852',
47558         
47559         /**
47560          * @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
47561          */
47562         preferedCountries: false,
47563         
47564         getAutoCreate : function()
47565         {
47566             var data = Roo.bootstrap.form.PhoneInputData();
47567             var align = this.labelAlign || this.parentLabelAlign();
47568             var id = Roo.id();
47569             
47570             this.allCountries = [];
47571             this.dialCodeMapping = [];
47572             
47573             for (var i = 0; i < data.length; i++) {
47574               var c = data[i];
47575               this.allCountries[i] = {
47576                 name: c[0],
47577                 iso2: c[1],
47578                 dialCode: c[2],
47579                 priority: c[3] || 0,
47580                 areaCodes: c[4] || null
47581               };
47582               this.dialCodeMapping[c[2]] = {
47583                   name: c[0],
47584                   iso2: c[1],
47585                   priority: c[3] || 0,
47586                   areaCodes: c[4] || null
47587               };
47588             }
47589             
47590             var cfg = {
47591                 cls: 'form-group',
47592                 cn: []
47593             };
47594             
47595             var input =  {
47596                 tag: 'input',
47597                 id : id,
47598                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
47599                 maxlength: this.max_length,
47600                 cls : 'form-control tel-input',
47601                 autocomplete: 'new-password'
47602             };
47603             
47604             var hiddenInput = {
47605                 tag: 'input',
47606                 type: 'hidden',
47607                 cls: 'hidden-tel-input'
47608             };
47609             
47610             if (this.name) {
47611                 hiddenInput.name = this.name;
47612             }
47613             
47614             if (this.disabled) {
47615                 input.disabled = true;
47616             }
47617             
47618             var flag_container = {
47619                 tag: 'div',
47620                 cls: 'flag-box',
47621                 cn: [
47622                     {
47623                         tag: 'div',
47624                         cls: 'flag'
47625                     },
47626                     {
47627                         tag: 'div',
47628                         cls: 'caret'
47629                     }
47630                 ]
47631             };
47632             
47633             var box = {
47634                 tag: 'div',
47635                 cls: this.hasFeedback ? 'has-feedback' : '',
47636                 cn: [
47637                     hiddenInput,
47638                     input,
47639                     {
47640                         tag: 'input',
47641                         cls: 'dial-code-holder',
47642                         disabled: true
47643                     }
47644                 ]
47645             };
47646             
47647             var container = {
47648                 cls: 'roo-select2-container input-group',
47649                 cn: [
47650                     flag_container,
47651                     box
47652                 ]
47653             };
47654             
47655             if (this.fieldLabel.length) {
47656                 var indicator = {
47657                     tag: 'i',
47658                     tooltip: 'This field is required'
47659                 };
47660                 
47661                 var label = {
47662                     tag: 'label',
47663                     'for':  id,
47664                     cls: 'control-label',
47665                     cn: []
47666                 };
47667                 
47668                 var label_text = {
47669                     tag: 'span',
47670                     html: this.fieldLabel
47671                 };
47672                 
47673                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
47674                 label.cn = [
47675                     indicator,
47676                     label_text
47677                 ];
47678                 
47679                 if(this.indicatorpos == 'right') {
47680                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
47681                     label.cn = [
47682                         label_text,
47683                         indicator
47684                     ];
47685                 }
47686                 
47687                 if(align == 'left') {
47688                     container = {
47689                         tag: 'div',
47690                         cn: [
47691                             container
47692                         ]
47693                     };
47694                     
47695                     if(this.labelWidth > 12){
47696                         label.style = "width: " + this.labelWidth + 'px';
47697                     }
47698                     if(this.labelWidth < 13 && this.labelmd == 0){
47699                         this.labelmd = this.labelWidth;
47700                     }
47701                     if(this.labellg > 0){
47702                         label.cls += ' col-lg-' + this.labellg;
47703                         input.cls += ' col-lg-' + (12 - this.labellg);
47704                     }
47705                     if(this.labelmd > 0){
47706                         label.cls += ' col-md-' + this.labelmd;
47707                         container.cls += ' col-md-' + (12 - this.labelmd);
47708                     }
47709                     if(this.labelsm > 0){
47710                         label.cls += ' col-sm-' + this.labelsm;
47711                         container.cls += ' col-sm-' + (12 - this.labelsm);
47712                     }
47713                     if(this.labelxs > 0){
47714                         label.cls += ' col-xs-' + this.labelxs;
47715                         container.cls += ' col-xs-' + (12 - this.labelxs);
47716                     }
47717                 }
47718             }
47719             
47720             cfg.cn = [
47721                 label,
47722                 container
47723             ];
47724             
47725             var settings = this;
47726             
47727             ['xs','sm','md','lg'].map(function(size){
47728                 if (settings[size]) {
47729                     cfg.cls += ' col-' + size + '-' + settings[size];
47730                 }
47731             });
47732             
47733             this.store = new Roo.data.Store({
47734                 proxy : new Roo.data.MemoryProxy({}),
47735                 reader : new Roo.data.JsonReader({
47736                     fields : [
47737                         {
47738                             'name' : 'name',
47739                             'type' : 'string'
47740                         },
47741                         {
47742                             'name' : 'iso2',
47743                             'type' : 'string'
47744                         },
47745                         {
47746                             'name' : 'dialCode',
47747                             'type' : 'string'
47748                         },
47749                         {
47750                             'name' : 'priority',
47751                             'type' : 'string'
47752                         },
47753                         {
47754                             'name' : 'areaCodes',
47755                             'type' : 'string'
47756                         }
47757                     ]
47758                 })
47759             });
47760             
47761             if(!this.preferedCountries) {
47762                 this.preferedCountries = [
47763                     'hk',
47764                     'gb',
47765                     'us'
47766                 ];
47767             }
47768             
47769             var p = this.preferedCountries.reverse();
47770             
47771             if(p) {
47772                 for (var i = 0; i < p.length; i++) {
47773                     for (var j = 0; j < this.allCountries.length; j++) {
47774                         if(this.allCountries[j].iso2 == p[i]) {
47775                             var t = this.allCountries[j];
47776                             this.allCountries.splice(j,1);
47777                             this.allCountries.unshift(t);
47778                         }
47779                     } 
47780                 }
47781             }
47782             
47783             this.store.proxy.data = {
47784                 success: true,
47785                 data: this.allCountries
47786             };
47787             
47788             return cfg;
47789         },
47790         
47791         initEvents : function()
47792         {
47793             this.createList();
47794             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
47795             
47796             this.indicator = this.indicatorEl();
47797             this.flag = this.flagEl();
47798             this.dialCodeHolder = this.dialCodeHolderEl();
47799             
47800             this.trigger = this.el.select('div.flag-box',true).first();
47801             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
47802             
47803             var _this = this;
47804             
47805             (function(){
47806                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47807                 _this.list.setWidth(lw);
47808             }).defer(100);
47809             
47810             this.list.on('mouseover', this.onViewOver, this);
47811             this.list.on('mousemove', this.onViewMove, this);
47812             this.inputEl().on("keyup", this.onKeyUp, this);
47813             this.inputEl().on("keypress", this.onKeyPress, this);
47814             
47815             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
47816
47817             this.view = new Roo.View(this.list, this.tpl, {
47818                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47819             });
47820             
47821             this.view.on('click', this.onViewClick, this);
47822             this.setValue(this.defaultDialCode);
47823         },
47824         
47825         onTriggerClick : function(e)
47826         {
47827             Roo.log('trigger click');
47828             if(this.disabled){
47829                 return;
47830             }
47831             
47832             if(this.isExpanded()){
47833                 this.collapse();
47834                 this.hasFocus = false;
47835             }else {
47836                 this.store.load({});
47837                 this.hasFocus = true;
47838                 this.expand();
47839             }
47840         },
47841         
47842         isExpanded : function()
47843         {
47844             return this.list.isVisible();
47845         },
47846         
47847         collapse : function()
47848         {
47849             if(!this.isExpanded()){
47850                 return;
47851             }
47852             this.list.hide();
47853             Roo.get(document).un('mousedown', this.collapseIf, this);
47854             Roo.get(document).un('mousewheel', this.collapseIf, this);
47855             this.fireEvent('collapse', this);
47856             this.validate();
47857         },
47858         
47859         expand : function()
47860         {
47861             Roo.log('expand');
47862
47863             if(this.isExpanded() || !this.hasFocus){
47864                 return;
47865             }
47866             
47867             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
47868             this.list.setWidth(lw);
47869             
47870             this.list.show();
47871             this.restrictHeight();
47872             
47873             Roo.get(document).on('mousedown', this.collapseIf, this);
47874             Roo.get(document).on('mousewheel', this.collapseIf, this);
47875             
47876             this.fireEvent('expand', this);
47877         },
47878         
47879         restrictHeight : function()
47880         {
47881             this.list.alignTo(this.inputEl(), this.listAlign);
47882             this.list.alignTo(this.inputEl(), this.listAlign);
47883         },
47884         
47885         onViewOver : function(e, t)
47886         {
47887             if(this.inKeyMode){
47888                 return;
47889             }
47890             var item = this.view.findItemFromChild(t);
47891             
47892             if(item){
47893                 var index = this.view.indexOf(item);
47894                 this.select(index, false);
47895             }
47896         },
47897
47898         // private
47899         onViewClick : function(view, doFocus, el, e)
47900         {
47901             var index = this.view.getSelectedIndexes()[0];
47902             
47903             var r = this.store.getAt(index);
47904             
47905             if(r){
47906                 this.onSelect(r, index);
47907             }
47908             if(doFocus !== false && !this.blockFocus){
47909                 this.inputEl().focus();
47910             }
47911         },
47912         
47913         onViewMove : function(e, t)
47914         {
47915             this.inKeyMode = false;
47916         },
47917         
47918         select : function(index, scrollIntoView)
47919         {
47920             this.selectedIndex = index;
47921             this.view.select(index);
47922             if(scrollIntoView !== false){
47923                 var el = this.view.getNode(index);
47924                 if(el){
47925                     this.list.scrollChildIntoView(el, false);
47926                 }
47927             }
47928         },
47929         
47930         createList : function()
47931         {
47932             this.list = Roo.get(document.body).createChild({
47933                 tag: 'ul',
47934                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
47935                 style: 'display:none'
47936             });
47937             
47938             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
47939         },
47940         
47941         collapseIf : function(e)
47942         {
47943             var in_combo  = e.within(this.el);
47944             var in_list =  e.within(this.list);
47945             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
47946             
47947             if (in_combo || in_list || is_list) {
47948                 return;
47949             }
47950             this.collapse();
47951         },
47952         
47953         onSelect : function(record, index)
47954         {
47955             if(this.fireEvent('beforeselect', this, record, index) !== false){
47956                 
47957                 this.setFlagClass(record.data.iso2);
47958                 this.setDialCode(record.data.dialCode);
47959                 this.hasFocus = false;
47960                 this.collapse();
47961                 this.fireEvent('select', this, record, index);
47962             }
47963         },
47964         
47965         flagEl : function()
47966         {
47967             var flag = this.el.select('div.flag',true).first();
47968             if(!flag){
47969                 return false;
47970             }
47971             return flag;
47972         },
47973         
47974         dialCodeHolderEl : function()
47975         {
47976             var d = this.el.select('input.dial-code-holder',true).first();
47977             if(!d){
47978                 return false;
47979             }
47980             return d;
47981         },
47982         
47983         setDialCode : function(v)
47984         {
47985             this.dialCodeHolder.dom.value = '+'+v;
47986         },
47987         
47988         setFlagClass : function(n)
47989         {
47990             this.flag.dom.className = 'flag '+n;
47991         },
47992         
47993         getValue : function()
47994         {
47995             var v = this.inputEl().getValue();
47996             if(this.dialCodeHolder) {
47997                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
47998             }
47999             return v;
48000         },
48001         
48002         setValue : function(v)
48003         {
48004             var d = this.getDialCode(v);
48005             
48006             //invalid dial code
48007             if(v.length == 0 || !d || d.length == 0) {
48008                 if(this.rendered){
48009                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48010                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48011                 }
48012                 return;
48013             }
48014             
48015             //valid dial code
48016             this.setFlagClass(this.dialCodeMapping[d].iso2);
48017             this.setDialCode(d);
48018             this.inputEl().dom.value = v.replace('+'+d,'');
48019             this.hiddenEl().dom.value = this.getValue();
48020             
48021             this.validate();
48022         },
48023         
48024         getDialCode : function(v)
48025         {
48026             v = v ||  '';
48027             
48028             if (v.length == 0) {
48029                 return this.dialCodeHolder.dom.value;
48030             }
48031             
48032             var dialCode = "";
48033             if (v.charAt(0) != "+") {
48034                 return false;
48035             }
48036             var numericChars = "";
48037             for (var i = 1; i < v.length; i++) {
48038               var c = v.charAt(i);
48039               if (!isNaN(c)) {
48040                 numericChars += c;
48041                 if (this.dialCodeMapping[numericChars]) {
48042                   dialCode = v.substr(1, i);
48043                 }
48044                 if (numericChars.length == 4) {
48045                   break;
48046                 }
48047               }
48048             }
48049             return dialCode;
48050         },
48051         
48052         reset : function()
48053         {
48054             this.setValue(this.defaultDialCode);
48055             this.validate();
48056         },
48057         
48058         hiddenEl : function()
48059         {
48060             return this.el.select('input.hidden-tel-input',true).first();
48061         },
48062         
48063         // after setting val
48064         onKeyUp : function(e){
48065             this.setValue(this.getValue());
48066         },
48067         
48068         onKeyPress : function(e){
48069             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48070                 e.stopEvent();
48071             }
48072         }
48073         
48074 });
48075 /**
48076  * @class Roo.bootstrap.form.MoneyField
48077  * @extends Roo.bootstrap.form.ComboBox
48078  * Bootstrap MoneyField class
48079  * 
48080  * @constructor
48081  * Create a new MoneyField.
48082  * @param {Object} config Configuration options
48083  */
48084
48085 Roo.bootstrap.form.MoneyField = function(config) {
48086     
48087     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48088     
48089 };
48090
48091 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48092     
48093     /**
48094      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48095      */
48096     allowDecimals : true,
48097     /**
48098      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48099      */
48100     decimalSeparator : ".",
48101     /**
48102      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48103      */
48104     decimalPrecision : 0,
48105     /**
48106      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48107      */
48108     allowNegative : true,
48109     /**
48110      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48111      */
48112     allowZero: true,
48113     /**
48114      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48115      */
48116     minValue : Number.NEGATIVE_INFINITY,
48117     /**
48118      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48119      */
48120     maxValue : Number.MAX_VALUE,
48121     /**
48122      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48123      */
48124     minText : "The minimum value for this field is {0}",
48125     /**
48126      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48127      */
48128     maxText : "The maximum value for this field is {0}",
48129     /**
48130      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48131      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48132      */
48133     nanText : "{0} is not a valid number",
48134     /**
48135      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48136      */
48137     castInt : true,
48138     /**
48139      * @cfg {String} defaults currency of the MoneyField
48140      * value should be in lkey
48141      */
48142     defaultCurrency : false,
48143     /**
48144      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48145      */
48146     thousandsDelimiter : false,
48147     /**
48148      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48149      */
48150     max_length: false,
48151     
48152     inputlg : 9,
48153     inputmd : 9,
48154     inputsm : 9,
48155     inputxs : 6,
48156      /**
48157      * @cfg {Roo.data.Store} store  Store to lookup currency??
48158      */
48159     store : false,
48160     
48161     getAutoCreate : function()
48162     {
48163         var align = this.labelAlign || this.parentLabelAlign();
48164         
48165         var id = Roo.id();
48166
48167         var cfg = {
48168             cls: 'form-group',
48169             cn: []
48170         };
48171
48172         var input =  {
48173             tag: 'input',
48174             id : id,
48175             cls : 'form-control roo-money-amount-input',
48176             autocomplete: 'new-password'
48177         };
48178         
48179         var hiddenInput = {
48180             tag: 'input',
48181             type: 'hidden',
48182             id: Roo.id(),
48183             cls: 'hidden-number-input'
48184         };
48185         
48186         if(this.max_length) {
48187             input.maxlength = this.max_length; 
48188         }
48189         
48190         if (this.name) {
48191             hiddenInput.name = this.name;
48192         }
48193
48194         if (this.disabled) {
48195             input.disabled = true;
48196         }
48197
48198         var clg = 12 - this.inputlg;
48199         var cmd = 12 - this.inputmd;
48200         var csm = 12 - this.inputsm;
48201         var cxs = 12 - this.inputxs;
48202         
48203         var container = {
48204             tag : 'div',
48205             cls : 'row roo-money-field',
48206             cn : [
48207                 {
48208                     tag : 'div',
48209                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48210                     cn : [
48211                         {
48212                             tag : 'div',
48213                             cls: 'roo-select2-container input-group',
48214                             cn: [
48215                                 {
48216                                     tag : 'input',
48217                                     cls : 'form-control roo-money-currency-input',
48218                                     autocomplete: 'new-password',
48219                                     readOnly : 1,
48220                                     name : this.currencyName
48221                                 },
48222                                 {
48223                                     tag :'span',
48224                                     cls : 'input-group-addon',
48225                                     cn : [
48226                                         {
48227                                             tag: 'span',
48228                                             cls: 'caret'
48229                                         }
48230                                     ]
48231                                 }
48232                             ]
48233                         }
48234                     ]
48235                 },
48236                 {
48237                     tag : 'div',
48238                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48239                     cn : [
48240                         {
48241                             tag: 'div',
48242                             cls: this.hasFeedback ? 'has-feedback' : '',
48243                             cn: [
48244                                 input
48245                             ]
48246                         }
48247                     ]
48248                 }
48249             ]
48250             
48251         };
48252         
48253         if (this.fieldLabel.length) {
48254             var indicator = {
48255                 tag: 'i',
48256                 tooltip: 'This field is required'
48257             };
48258
48259             var label = {
48260                 tag: 'label',
48261                 'for':  id,
48262                 cls: 'control-label',
48263                 cn: []
48264             };
48265
48266             var label_text = {
48267                 tag: 'span',
48268                 html: this.fieldLabel
48269             };
48270
48271             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48272             label.cn = [
48273                 indicator,
48274                 label_text
48275             ];
48276
48277             if(this.indicatorpos == 'right') {
48278                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48279                 label.cn = [
48280                     label_text,
48281                     indicator
48282                 ];
48283             }
48284
48285             if(align == 'left') {
48286                 container = {
48287                     tag: 'div',
48288                     cn: [
48289                         container
48290                     ]
48291                 };
48292
48293                 if(this.labelWidth > 12){
48294                     label.style = "width: " + this.labelWidth + 'px';
48295                 }
48296                 if(this.labelWidth < 13 && this.labelmd == 0){
48297                     this.labelmd = this.labelWidth;
48298                 }
48299                 if(this.labellg > 0){
48300                     label.cls += ' col-lg-' + this.labellg;
48301                     input.cls += ' col-lg-' + (12 - this.labellg);
48302                 }
48303                 if(this.labelmd > 0){
48304                     label.cls += ' col-md-' + this.labelmd;
48305                     container.cls += ' col-md-' + (12 - this.labelmd);
48306                 }
48307                 if(this.labelsm > 0){
48308                     label.cls += ' col-sm-' + this.labelsm;
48309                     container.cls += ' col-sm-' + (12 - this.labelsm);
48310                 }
48311                 if(this.labelxs > 0){
48312                     label.cls += ' col-xs-' + this.labelxs;
48313                     container.cls += ' col-xs-' + (12 - this.labelxs);
48314                 }
48315             }
48316         }
48317
48318         cfg.cn = [
48319             label,
48320             container,
48321             hiddenInput
48322         ];
48323         
48324         var settings = this;
48325
48326         ['xs','sm','md','lg'].map(function(size){
48327             if (settings[size]) {
48328                 cfg.cls += ' col-' + size + '-' + settings[size];
48329             }
48330         });
48331         
48332         return cfg;
48333     },
48334     
48335     initEvents : function()
48336     {
48337         this.indicator = this.indicatorEl();
48338         
48339         this.initCurrencyEvent();
48340         
48341         this.initNumberEvent();
48342     },
48343     
48344     initCurrencyEvent : function()
48345     {
48346         if (!this.store) {
48347             throw "can not find store for combo";
48348         }
48349         
48350         this.store = Roo.factory(this.store, Roo.data);
48351         this.store.parent = this;
48352         
48353         this.createList();
48354         
48355         this.triggerEl = this.el.select('.input-group-addon', true).first();
48356         
48357         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48358         
48359         var _this = this;
48360         
48361         (function(){
48362             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48363             _this.list.setWidth(lw);
48364         }).defer(100);
48365         
48366         this.list.on('mouseover', this.onViewOver, this);
48367         this.list.on('mousemove', this.onViewMove, this);
48368         this.list.on('scroll', this.onViewScroll, this);
48369         
48370         if(!this.tpl){
48371             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48372         }
48373         
48374         this.view = new Roo.View(this.list, this.tpl, {
48375             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48376         });
48377         
48378         this.view.on('click', this.onViewClick, this);
48379         
48380         this.store.on('beforeload', this.onBeforeLoad, this);
48381         this.store.on('load', this.onLoad, this);
48382         this.store.on('loadexception', this.onLoadException, this);
48383         
48384         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48385             "up" : function(e){
48386                 this.inKeyMode = true;
48387                 this.selectPrev();
48388             },
48389
48390             "down" : function(e){
48391                 if(!this.isExpanded()){
48392                     this.onTriggerClick();
48393                 }else{
48394                     this.inKeyMode = true;
48395                     this.selectNext();
48396                 }
48397             },
48398
48399             "enter" : function(e){
48400                 this.collapse();
48401                 
48402                 if(this.fireEvent("specialkey", this, e)){
48403                     this.onViewClick(false);
48404                 }
48405                 
48406                 return true;
48407             },
48408
48409             "esc" : function(e){
48410                 this.collapse();
48411             },
48412
48413             "tab" : function(e){
48414                 this.collapse();
48415                 
48416                 if(this.fireEvent("specialkey", this, e)){
48417                     this.onViewClick(false);
48418                 }
48419                 
48420                 return true;
48421             },
48422
48423             scope : this,
48424
48425             doRelay : function(foo, bar, hname){
48426                 if(hname == 'down' || this.scope.isExpanded()){
48427                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48428                 }
48429                 return true;
48430             },
48431
48432             forceKeyDown: true
48433         });
48434         
48435         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48436         
48437     },
48438     
48439     initNumberEvent : function(e)
48440     {
48441         this.inputEl().on("keydown" , this.fireKey,  this);
48442         this.inputEl().on("focus", this.onFocus,  this);
48443         this.inputEl().on("blur", this.onBlur,  this);
48444         
48445         this.inputEl().relayEvent('keyup', this);
48446         
48447         if(this.indicator){
48448             this.indicator.addClass('invisible');
48449         }
48450  
48451         this.originalValue = this.getValue();
48452         
48453         if(this.validationEvent == 'keyup'){
48454             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48455             this.inputEl().on('keyup', this.filterValidation, this);
48456         }
48457         else if(this.validationEvent !== false){
48458             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48459         }
48460         
48461         if(this.selectOnFocus){
48462             this.on("focus", this.preFocus, this);
48463             
48464         }
48465         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48466             this.inputEl().on("keypress", this.filterKeys, this);
48467         } else {
48468             this.inputEl().relayEvent('keypress', this);
48469         }
48470         
48471         var allowed = "0123456789";
48472         
48473         if(this.allowDecimals){
48474             allowed += this.decimalSeparator;
48475         }
48476         
48477         if(this.allowNegative){
48478             allowed += "-";
48479         }
48480         
48481         if(this.thousandsDelimiter) {
48482             allowed += ",";
48483         }
48484         
48485         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48486         
48487         var keyPress = function(e){
48488             
48489             var k = e.getKey();
48490             
48491             var c = e.getCharCode();
48492             
48493             if(
48494                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48495                     allowed.indexOf(String.fromCharCode(c)) === -1
48496             ){
48497                 e.stopEvent();
48498                 return;
48499             }
48500             
48501             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48502                 return;
48503             }
48504             
48505             if(allowed.indexOf(String.fromCharCode(c)) === -1){
48506                 e.stopEvent();
48507             }
48508         };
48509         
48510         this.inputEl().on("keypress", keyPress, this);
48511         
48512     },
48513     
48514     onTriggerClick : function(e)
48515     {   
48516         if(this.disabled){
48517             return;
48518         }
48519         
48520         this.page = 0;
48521         this.loadNext = false;
48522         
48523         if(this.isExpanded()){
48524             this.collapse();
48525             return;
48526         }
48527         
48528         this.hasFocus = true;
48529         
48530         if(this.triggerAction == 'all') {
48531             this.doQuery(this.allQuery, true);
48532             return;
48533         }
48534         
48535         this.doQuery(this.getRawValue());
48536     },
48537     
48538     getCurrency : function()
48539     {   
48540         var v = this.currencyEl().getValue();
48541         
48542         return v;
48543     },
48544     
48545     restrictHeight : function()
48546     {
48547         this.list.alignTo(this.currencyEl(), this.listAlign);
48548         this.list.alignTo(this.currencyEl(), this.listAlign);
48549     },
48550     
48551     onViewClick : function(view, doFocus, el, e)
48552     {
48553         var index = this.view.getSelectedIndexes()[0];
48554         
48555         var r = this.store.getAt(index);
48556         
48557         if(r){
48558             this.onSelect(r, index);
48559         }
48560     },
48561     
48562     onSelect : function(record, index){
48563         
48564         if(this.fireEvent('beforeselect', this, record, index) !== false){
48565         
48566             this.setFromCurrencyData(index > -1 ? record.data : false);
48567             
48568             this.collapse();
48569             
48570             this.fireEvent('select', this, record, index);
48571         }
48572     },
48573     
48574     setFromCurrencyData : function(o)
48575     {
48576         var currency = '';
48577         
48578         this.lastCurrency = o;
48579         
48580         if (this.currencyField) {
48581             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
48582         } else {
48583             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
48584         }
48585         
48586         this.lastSelectionText = currency;
48587         
48588         //setting default currency
48589         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
48590             this.setCurrency(this.defaultCurrency);
48591             return;
48592         }
48593         
48594         this.setCurrency(currency);
48595     },
48596     
48597     setFromData : function(o)
48598     {
48599         var c = {};
48600         
48601         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
48602         
48603         this.setFromCurrencyData(c);
48604         
48605         var value = '';
48606         
48607         if (this.name) {
48608             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
48609         } else {
48610             Roo.log('no value set for '+ (this.name ? this.name : this.id));
48611         }
48612         
48613         this.setValue(value);
48614         
48615     },
48616     
48617     setCurrency : function(v)
48618     {   
48619         this.currencyValue = v;
48620         
48621         if(this.rendered){
48622             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
48623             this.validate();
48624         }
48625     },
48626     
48627     setValue : function(v)
48628     {
48629         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
48630         
48631         this.value = v;
48632         
48633         if(this.rendered){
48634             
48635             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48636             
48637             this.inputEl().dom.value = (v == '') ? '' :
48638                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
48639             
48640             if(!this.allowZero && v === '0') {
48641                 this.hiddenEl().dom.value = '';
48642                 this.inputEl().dom.value = '';
48643             }
48644             
48645             this.validate();
48646         }
48647     },
48648     
48649     getRawValue : function()
48650     {
48651         var v = this.inputEl().getValue();
48652         
48653         return v;
48654     },
48655     
48656     getValue : function()
48657     {
48658         return this.fixPrecision(this.parseValue(this.getRawValue()));
48659     },
48660     
48661     parseValue : function(value)
48662     {
48663         if(this.thousandsDelimiter) {
48664             value += "";
48665             r = new RegExp(",", "g");
48666             value = value.replace(r, "");
48667         }
48668         
48669         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
48670         return isNaN(value) ? '' : value;
48671         
48672     },
48673     
48674     fixPrecision : function(value)
48675     {
48676         if(this.thousandsDelimiter) {
48677             value += "";
48678             r = new RegExp(",", "g");
48679             value = value.replace(r, "");
48680         }
48681         
48682         var nan = isNaN(value);
48683         
48684         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
48685             return nan ? '' : value;
48686         }
48687         return parseFloat(value).toFixed(this.decimalPrecision);
48688     },
48689     
48690     decimalPrecisionFcn : function(v)
48691     {
48692         return Math.floor(v);
48693     },
48694     
48695     validateValue : function(value)
48696     {
48697         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
48698             return false;
48699         }
48700         
48701         var num = this.parseValue(value);
48702         
48703         if(isNaN(num)){
48704             this.markInvalid(String.format(this.nanText, value));
48705             return false;
48706         }
48707         
48708         if(num < this.minValue){
48709             this.markInvalid(String.format(this.minText, this.minValue));
48710             return false;
48711         }
48712         
48713         if(num > this.maxValue){
48714             this.markInvalid(String.format(this.maxText, this.maxValue));
48715             return false;
48716         }
48717         
48718         return true;
48719     },
48720     
48721     validate : function()
48722     {
48723         if(this.disabled || this.allowBlank){
48724             this.markValid();
48725             return true;
48726         }
48727         
48728         var currency = this.getCurrency();
48729         
48730         if(this.validateValue(this.getRawValue()) && currency.length){
48731             this.markValid();
48732             return true;
48733         }
48734         
48735         this.markInvalid();
48736         return false;
48737     },
48738     
48739     getName: function()
48740     {
48741         return this.name;
48742     },
48743     
48744     beforeBlur : function()
48745     {
48746         if(!this.castInt){
48747             return;
48748         }
48749         
48750         var v = this.parseValue(this.getRawValue());
48751         
48752         if(v || v == 0){
48753             this.setValue(v);
48754         }
48755     },
48756     
48757     onBlur : function()
48758     {
48759         this.beforeBlur();
48760         
48761         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
48762             //this.el.removeClass(this.focusClass);
48763         }
48764         
48765         this.hasFocus = false;
48766         
48767         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
48768             this.validate();
48769         }
48770         
48771         var v = this.getValue();
48772         
48773         if(String(v) !== String(this.startValue)){
48774             this.fireEvent('change', this, v, this.startValue);
48775         }
48776         
48777         this.fireEvent("blur", this);
48778     },
48779     
48780     inputEl : function()
48781     {
48782         return this.el.select('.roo-money-amount-input', true).first();
48783     },
48784     
48785     currencyEl : function()
48786     {
48787         return this.el.select('.roo-money-currency-input', true).first();
48788     },
48789     
48790     hiddenEl : function()
48791     {
48792         return this.el.select('input.hidden-number-input',true).first();
48793     }
48794     
48795 });/**
48796  * @class Roo.bootstrap.BezierSignature
48797  * @extends Roo.bootstrap.Component
48798  * Bootstrap BezierSignature class
48799  * This script refer to:
48800  *    Title: Signature Pad
48801  *    Author: szimek
48802  *    Availability: https://github.com/szimek/signature_pad
48803  *
48804  * @constructor
48805  * Create a new BezierSignature
48806  * @param {Object} config The config object
48807  */
48808
48809 Roo.bootstrap.BezierSignature = function(config){
48810     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
48811     this.addEvents({
48812         "resize" : true
48813     });
48814 };
48815
48816 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
48817 {
48818      
48819     curve_data: [],
48820     
48821     is_empty: true,
48822     
48823     mouse_btn_down: true,
48824     
48825     /**
48826      * @cfg {int} canvas height
48827      */
48828     canvas_height: '200px',
48829     
48830     /**
48831      * @cfg {float|function} Radius of a single dot.
48832      */ 
48833     dot_size: false,
48834     
48835     /**
48836      * @cfg {float} Minimum width of a line. Defaults to 0.5.
48837      */
48838     min_width: 0.5,
48839     
48840     /**
48841      * @cfg {float} Maximum width of a line. Defaults to 2.5.
48842      */
48843     max_width: 2.5,
48844     
48845     /**
48846      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
48847      */
48848     throttle: 16,
48849     
48850     /**
48851      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
48852      */
48853     min_distance: 5,
48854     
48855     /**
48856      * @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.
48857      */
48858     bg_color: 'rgba(0, 0, 0, 0)',
48859     
48860     /**
48861      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
48862      */
48863     dot_color: 'black',
48864     
48865     /**
48866      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
48867      */ 
48868     velocity_filter_weight: 0.7,
48869     
48870     /**
48871      * @cfg {function} Callback when stroke begin. 
48872      */
48873     onBegin: false,
48874     
48875     /**
48876      * @cfg {function} Callback when stroke end.
48877      */
48878     onEnd: false,
48879     
48880     getAutoCreate : function()
48881     {
48882         var cls = 'roo-signature column';
48883         
48884         if(this.cls){
48885             cls += ' ' + this.cls;
48886         }
48887         
48888         var col_sizes = [
48889             'lg',
48890             'md',
48891             'sm',
48892             'xs'
48893         ];
48894         
48895         for(var i = 0; i < col_sizes.length; i++) {
48896             if(this[col_sizes[i]]) {
48897                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
48898             }
48899         }
48900         
48901         var cfg = {
48902             tag: 'div',
48903             cls: cls,
48904             cn: [
48905                 {
48906                     tag: 'div',
48907                     cls: 'roo-signature-body',
48908                     cn: [
48909                         {
48910                             tag: 'canvas',
48911                             cls: 'roo-signature-body-canvas',
48912                             height: this.canvas_height,
48913                             width: this.canvas_width
48914                         }
48915                     ]
48916                 },
48917                 {
48918                     tag: 'input',
48919                     type: 'file',
48920                     style: 'display: none'
48921                 }
48922             ]
48923         };
48924         
48925         return cfg;
48926     },
48927     
48928     initEvents: function() 
48929     {
48930         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
48931         
48932         var canvas = this.canvasEl();
48933         
48934         // mouse && touch event swapping...
48935         canvas.dom.style.touchAction = 'none';
48936         canvas.dom.style.msTouchAction = 'none';
48937         
48938         this.mouse_btn_down = false;
48939         canvas.on('mousedown', this._handleMouseDown, this);
48940         canvas.on('mousemove', this._handleMouseMove, this);
48941         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
48942         
48943         if (window.PointerEvent) {
48944             canvas.on('pointerdown', this._handleMouseDown, this);
48945             canvas.on('pointermove', this._handleMouseMove, this);
48946             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
48947         }
48948         
48949         if ('ontouchstart' in window) {
48950             canvas.on('touchstart', this._handleTouchStart, this);
48951             canvas.on('touchmove', this._handleTouchMove, this);
48952             canvas.on('touchend', this._handleTouchEnd, this);
48953         }
48954         
48955         Roo.EventManager.onWindowResize(this.resize, this, true);
48956         
48957         // file input event
48958         this.fileEl().on('change', this.uploadImage, this);
48959         
48960         this.clear();
48961         
48962         this.resize();
48963     },
48964     
48965     resize: function(){
48966         
48967         var canvas = this.canvasEl().dom;
48968         var ctx = this.canvasElCtx();
48969         var img_data = false;
48970         
48971         if(canvas.width > 0) {
48972             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
48973         }
48974         // setting canvas width will clean img data
48975         canvas.width = 0;
48976         
48977         var style = window.getComputedStyle ? 
48978             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
48979             
48980         var padding_left = parseInt(style.paddingLeft) || 0;
48981         var padding_right = parseInt(style.paddingRight) || 0;
48982         
48983         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
48984         
48985         if(img_data) {
48986             ctx.putImageData(img_data, 0, 0);
48987         }
48988     },
48989     
48990     _handleMouseDown: function(e)
48991     {
48992         if (e.browserEvent.which === 1) {
48993             this.mouse_btn_down = true;
48994             this.strokeBegin(e);
48995         }
48996     },
48997     
48998     _handleMouseMove: function (e)
48999     {
49000         if (this.mouse_btn_down) {
49001             this.strokeMoveUpdate(e);
49002         }
49003     },
49004     
49005     _handleMouseUp: function (e)
49006     {
49007         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49008             this.mouse_btn_down = false;
49009             this.strokeEnd(e);
49010         }
49011     },
49012     
49013     _handleTouchStart: function (e) {
49014         
49015         e.preventDefault();
49016         if (e.browserEvent.targetTouches.length === 1) {
49017             // var touch = e.browserEvent.changedTouches[0];
49018             // this.strokeBegin(touch);
49019             
49020              this.strokeBegin(e); // assume e catching the correct xy...
49021         }
49022     },
49023     
49024     _handleTouchMove: function (e) {
49025         e.preventDefault();
49026         // var touch = event.targetTouches[0];
49027         // _this._strokeMoveUpdate(touch);
49028         this.strokeMoveUpdate(e);
49029     },
49030     
49031     _handleTouchEnd: function (e) {
49032         var wasCanvasTouched = e.target === this.canvasEl().dom;
49033         if (wasCanvasTouched) {
49034             e.preventDefault();
49035             // var touch = event.changedTouches[0];
49036             // _this._strokeEnd(touch);
49037             this.strokeEnd(e);
49038         }
49039     },
49040     
49041     reset: function () {
49042         this._lastPoints = [];
49043         this._lastVelocity = 0;
49044         this._lastWidth = (this.min_width + this.max_width) / 2;
49045         this.canvasElCtx().fillStyle = this.dot_color;
49046     },
49047     
49048     strokeMoveUpdate: function(e)
49049     {
49050         this.strokeUpdate(e);
49051         
49052         if (this.throttle) {
49053             this.throttleStroke(this.strokeUpdate, this.throttle);
49054         }
49055         else {
49056             this.strokeUpdate(e);
49057         }
49058     },
49059     
49060     strokeBegin: function(e)
49061     {
49062         var newPointGroup = {
49063             color: this.dot_color,
49064             points: []
49065         };
49066         
49067         if (typeof this.onBegin === 'function') {
49068             this.onBegin(e);
49069         }
49070         
49071         this.curve_data.push(newPointGroup);
49072         this.reset();
49073         this.strokeUpdate(e);
49074     },
49075     
49076     strokeUpdate: function(e)
49077     {
49078         var rect = this.canvasEl().dom.getBoundingClientRect();
49079         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49080         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49081         var lastPoints = lastPointGroup.points;
49082         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49083         var isLastPointTooClose = lastPoint
49084             ? point.distanceTo(lastPoint) <= this.min_distance
49085             : false;
49086         var color = lastPointGroup.color;
49087         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49088             var curve = this.addPoint(point);
49089             if (!lastPoint) {
49090                 this.drawDot({color: color, point: point});
49091             }
49092             else if (curve) {
49093                 this.drawCurve({color: color, curve: curve});
49094             }
49095             lastPoints.push({
49096                 time: point.time,
49097                 x: point.x,
49098                 y: point.y
49099             });
49100         }
49101     },
49102     
49103     strokeEnd: function(e)
49104     {
49105         this.strokeUpdate(e);
49106         if (typeof this.onEnd === 'function') {
49107             this.onEnd(e);
49108         }
49109     },
49110     
49111     addPoint:  function (point) {
49112         var _lastPoints = this._lastPoints;
49113         _lastPoints.push(point);
49114         if (_lastPoints.length > 2) {
49115             if (_lastPoints.length === 3) {
49116                 _lastPoints.unshift(_lastPoints[0]);
49117             }
49118             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49119             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49120             _lastPoints.shift();
49121             return curve;
49122         }
49123         return null;
49124     },
49125     
49126     calculateCurveWidths: function (startPoint, endPoint) {
49127         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49128             (1 - this.velocity_filter_weight) * this._lastVelocity;
49129
49130         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49131         var widths = {
49132             end: newWidth,
49133             start: this._lastWidth
49134         };
49135         
49136         this._lastVelocity = velocity;
49137         this._lastWidth = newWidth;
49138         return widths;
49139     },
49140     
49141     drawDot: function (_a) {
49142         var color = _a.color, point = _a.point;
49143         var ctx = this.canvasElCtx();
49144         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49145         ctx.beginPath();
49146         this.drawCurveSegment(point.x, point.y, width);
49147         ctx.closePath();
49148         ctx.fillStyle = color;
49149         ctx.fill();
49150     },
49151     
49152     drawCurve: function (_a) {
49153         var color = _a.color, curve = _a.curve;
49154         var ctx = this.canvasElCtx();
49155         var widthDelta = curve.endWidth - curve.startWidth;
49156         var drawSteps = Math.floor(curve.length()) * 2;
49157         ctx.beginPath();
49158         ctx.fillStyle = color;
49159         for (var i = 0; i < drawSteps; i += 1) {
49160         var t = i / drawSteps;
49161         var tt = t * t;
49162         var ttt = tt * t;
49163         var u = 1 - t;
49164         var uu = u * u;
49165         var uuu = uu * u;
49166         var x = uuu * curve.startPoint.x;
49167         x += 3 * uu * t * curve.control1.x;
49168         x += 3 * u * tt * curve.control2.x;
49169         x += ttt * curve.endPoint.x;
49170         var y = uuu * curve.startPoint.y;
49171         y += 3 * uu * t * curve.control1.y;
49172         y += 3 * u * tt * curve.control2.y;
49173         y += ttt * curve.endPoint.y;
49174         var width = curve.startWidth + ttt * widthDelta;
49175         this.drawCurveSegment(x, y, width);
49176         }
49177         ctx.closePath();
49178         ctx.fill();
49179     },
49180     
49181     drawCurveSegment: function (x, y, width) {
49182         var ctx = this.canvasElCtx();
49183         ctx.moveTo(x, y);
49184         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49185         this.is_empty = false;
49186     },
49187     
49188     clear: function()
49189     {
49190         var ctx = this.canvasElCtx();
49191         var canvas = this.canvasEl().dom;
49192         ctx.fillStyle = this.bg_color;
49193         ctx.clearRect(0, 0, canvas.width, canvas.height);
49194         ctx.fillRect(0, 0, canvas.width, canvas.height);
49195         this.curve_data = [];
49196         this.reset();
49197         this.is_empty = true;
49198     },
49199     
49200     fileEl: function()
49201     {
49202         return  this.el.select('input',true).first();
49203     },
49204     
49205     canvasEl: function()
49206     {
49207         return this.el.select('canvas',true).first();
49208     },
49209     
49210     canvasElCtx: function()
49211     {
49212         return this.el.select('canvas',true).first().dom.getContext('2d');
49213     },
49214     
49215     getImage: function(type)
49216     {
49217         if(this.is_empty) {
49218             return false;
49219         }
49220         
49221         // encryption ?
49222         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49223     },
49224     
49225     drawFromImage: function(img_src)
49226     {
49227         var img = new Image();
49228         
49229         img.onload = function(){
49230             this.canvasElCtx().drawImage(img, 0, 0);
49231         }.bind(this);
49232         
49233         img.src = img_src;
49234         
49235         this.is_empty = false;
49236     },
49237     
49238     selectImage: function()
49239     {
49240         this.fileEl().dom.click();
49241     },
49242     
49243     uploadImage: function(e)
49244     {
49245         var reader = new FileReader();
49246         
49247         reader.onload = function(e){
49248             var img = new Image();
49249             img.onload = function(){
49250                 this.reset();
49251                 this.canvasElCtx().drawImage(img, 0, 0);
49252             }.bind(this);
49253             img.src = e.target.result;
49254         }.bind(this);
49255         
49256         reader.readAsDataURL(e.target.files[0]);
49257     },
49258     
49259     // Bezier Point Constructor
49260     Point: (function () {
49261         function Point(x, y, time) {
49262             this.x = x;
49263             this.y = y;
49264             this.time = time || Date.now();
49265         }
49266         Point.prototype.distanceTo = function (start) {
49267             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49268         };
49269         Point.prototype.equals = function (other) {
49270             return this.x === other.x && this.y === other.y && this.time === other.time;
49271         };
49272         Point.prototype.velocityFrom = function (start) {
49273             return this.time !== start.time
49274             ? this.distanceTo(start) / (this.time - start.time)
49275             : 0;
49276         };
49277         return Point;
49278     }()),
49279     
49280     
49281     // Bezier Constructor
49282     Bezier: (function () {
49283         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49284             this.startPoint = startPoint;
49285             this.control2 = control2;
49286             this.control1 = control1;
49287             this.endPoint = endPoint;
49288             this.startWidth = startWidth;
49289             this.endWidth = endWidth;
49290         }
49291         Bezier.fromPoints = function (points, widths, scope) {
49292             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49293             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49294             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49295         };
49296         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49297             var dx1 = s1.x - s2.x;
49298             var dy1 = s1.y - s2.y;
49299             var dx2 = s2.x - s3.x;
49300             var dy2 = s2.y - s3.y;
49301             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49302             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49303             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49304             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49305             var dxm = m1.x - m2.x;
49306             var dym = m1.y - m2.y;
49307             var k = l2 / (l1 + l2);
49308             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49309             var tx = s2.x - cm.x;
49310             var ty = s2.y - cm.y;
49311             return {
49312                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49313                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49314             };
49315         };
49316         Bezier.prototype.length = function () {
49317             var steps = 10;
49318             var length = 0;
49319             var px;
49320             var py;
49321             for (var i = 0; i <= steps; i += 1) {
49322                 var t = i / steps;
49323                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49324                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49325                 if (i > 0) {
49326                     var xdiff = cx - px;
49327                     var ydiff = cy - py;
49328                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49329                 }
49330                 px = cx;
49331                 py = cy;
49332             }
49333             return length;
49334         };
49335         Bezier.prototype.point = function (t, start, c1, c2, end) {
49336             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49337             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49338             + (3.0 * c2 * (1.0 - t) * t * t)
49339             + (end * t * t * t);
49340         };
49341         return Bezier;
49342     }()),
49343     
49344     throttleStroke: function(fn, wait) {
49345       if (wait === void 0) { wait = 250; }
49346       var previous = 0;
49347       var timeout = null;
49348       var result;
49349       var storedContext;
49350       var storedArgs;
49351       var later = function () {
49352           previous = Date.now();
49353           timeout = null;
49354           result = fn.apply(storedContext, storedArgs);
49355           if (!timeout) {
49356               storedContext = null;
49357               storedArgs = [];
49358           }
49359       };
49360       return function wrapper() {
49361           var args = [];
49362           for (var _i = 0; _i < arguments.length; _i++) {
49363               args[_i] = arguments[_i];
49364           }
49365           var now = Date.now();
49366           var remaining = wait - (now - previous);
49367           storedContext = this;
49368           storedArgs = args;
49369           if (remaining <= 0 || remaining > wait) {
49370               if (timeout) {
49371                   clearTimeout(timeout);
49372                   timeout = null;
49373               }
49374               previous = now;
49375               result = fn.apply(storedContext, storedArgs);
49376               if (!timeout) {
49377                   storedContext = null;
49378                   storedArgs = [];
49379               }
49380           }
49381           else if (!timeout) {
49382               timeout = window.setTimeout(later, remaining);
49383           }
49384           return result;
49385       };
49386   }
49387   
49388 });
49389
49390  
49391
49392  // old names for form elements
49393 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49394 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49395 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49396 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49397 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49398 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49399 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49400 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49401 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49402 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49403 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49404 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49405 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49406 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49407 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49408 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49409 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49410 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49411 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49412 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49413 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49414 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49415 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49416 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49417 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49418 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49419
49420 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49421 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49422
49423 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49424 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49425
49426 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49427 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49428 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49429 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49430