fix #7391 -New_Customer_Portal_30_Login
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @children Roo.bootstrap.Component 
859  * @parent none builder
860  * Bootstrap Body class
861  *
862  * @constructor
863  * Create a new body
864  * @param {Object} config The config object
865  */
866
867 Roo.bootstrap.Body = function(config){
868
869     config = config || {};
870
871     Roo.bootstrap.Body.superclass.constructor.call(this, config);
872     this.el = Roo.get(config.el ? config.el : document.body );
873     if (this.cls && this.cls.length) {
874         Roo.get(document.body).addClass(this.cls);
875     }
876 };
877
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
879
880     is_body : true,// just to make sure it's constructed?
881
882         autoCreate : {
883         cls: 'container'
884     },
885     onRender : function(ct, position)
886     {
887        /* Roo.log("Roo.bootstrap.Body - onRender");
888         if (this.cls && this.cls.length) {
889             Roo.get(document.body).addClass(this.cls);
890         }
891         // style??? xttr???
892         */
893     }
894
895
896
897
898 });
899 /*
900  * - LGPL
901  *
902  * button group
903  * 
904  */
905
906
907 /**
908  * @class Roo.bootstrap.ButtonGroup
909  * @extends Roo.bootstrap.Component
910  * Bootstrap ButtonGroup class
911  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
912  * 
913  * @cfg {String} size lg | sm | xs (default empty normal)
914  * @cfg {String} align vertical | justified  (default none)
915  * @cfg {String} direction up | down (default down)
916  * @cfg {Boolean} toolbar false | true
917  * @cfg {Boolean} btn true | false
918  * 
919  * 
920  * @constructor
921  * Create a new Input
922  * @param {Object} config The config object
923  */
924
925 Roo.bootstrap.ButtonGroup = function(config){
926     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 };
928
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
930     
931     size: '',
932     align: '',
933     direction: '',
934     toolbar: false,
935     btn: true,
936
937     getAutoCreate : function(){
938         var cfg = {
939             cls: 'btn-group',
940             html : null
941         };
942         
943         cfg.html = this.html || cfg.html;
944         
945         if (this.toolbar) {
946             cfg = {
947                 cls: 'btn-toolbar',
948                 html: null
949             };
950             
951             return cfg;
952         }
953         
954         if (['vertical','justified'].indexOf(this.align)!==-1) {
955             cfg.cls = 'btn-group-' + this.align;
956             
957             if (this.align == 'justified') {
958                 console.log(this.items);
959             }
960         }
961         
962         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963             cfg.cls += ' btn-group-' + this.size;
964         }
965         
966         if (this.direction == 'up') {
967             cfg.cls += ' dropup' ;
968         }
969         
970         return cfg;
971     },
972     /**
973      * Add a button to the group (similar to NavItem API.)
974      */
975     addItem : function(cfg)
976     {
977         var cn = new Roo.bootstrap.Button(cfg);
978         //this.register(cn);
979         cn.parentId = this.id;
980         cn.onRender(this.el, null);
981         return cn;
982     }
983    
984 });
985
986  /*
987  * - LGPL
988  *
989  * button
990  * 
991  */
992
993 /**
994  * @class Roo.bootstrap.Button
995  * @extends Roo.bootstrap.Component
996  * Bootstrap Button class
997  * @cfg {String} html The button content
998  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001  * @cfg {String} size (lg|sm|xs)
1002  * @cfg {String} tag (a|input|submit)
1003  * @cfg {String} href empty or href
1004  * @cfg {Boolean} disabled default false;
1005  * @cfg {Boolean} isClose default false;
1006  * @cfg {String} glyphicon depricated - use fa
1007  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008  * @cfg {String} badge text for badge
1009  * @cfg {String} theme (default|glow)  
1010  * @cfg {Boolean} inverse dark themed version
1011  * @cfg {Boolean} toggle is it a slidy toggle button
1012  * @cfg {Boolean} pressed   default null - if the button ahs active state
1013  * @cfg {String} ontext text for on slidy toggle state
1014  * @cfg {String} offtext text for off slidy toggle state
1015  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1016  * @cfg {Boolean} removeClass remove the standard class..
1017  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1018  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1020
1021  * @constructor
1022  * Create a new button
1023  * @param {Object} config The config object
1024  */
1025
1026
1027 Roo.bootstrap.Button = function(config){
1028     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1029     
1030     this.addEvents({
1031         // raw events
1032         /**
1033          * @event click
1034          * When a button is pressed
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "click" : true,
1039         /**
1040          * @event dblclick
1041          * When a button is double clicked
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "dblclick" : true,
1046          /**
1047          * @event toggle
1048          * After the button has been toggles
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          * @param {boolean} pressed (also available as button.pressed)
1052          */
1053         "toggle" : true
1054     });
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1058     html: false,
1059     active: false,
1060     weight: '',
1061     badge_weight: '',
1062     outline : false,
1063     size: '',
1064     tag: 'button',
1065     href: '',
1066     disabled: false,
1067     isClose: false,
1068     glyphicon: '',
1069     fa: '',
1070     badge: '',
1071     theme: 'default',
1072     inverse: false,
1073     
1074     toggle: false,
1075     ontext: 'ON',
1076     offtext: 'OFF',
1077     defaulton: true,
1078     preventDefault: true,
1079     removeClass: false,
1080     name: false,
1081     target: false,
1082     group : false,
1083      
1084     pressed : null,
1085      
1086     
1087     getAutoCreate : function(){
1088         
1089         var cfg = {
1090             tag : 'button',
1091             cls : 'roo-button',
1092             html: ''
1093         };
1094         
1095         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097             this.tag = 'button';
1098         } else {
1099             cfg.tag = this.tag;
1100         }
1101         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1102         
1103         if (this.toggle == true) {
1104             cfg={
1105                 tag: 'div',
1106                 cls: 'slider-frame roo-button',
1107                 cn: [
1108                     {
1109                         tag: 'span',
1110                         'data-on-text':'ON',
1111                         'data-off-text':'OFF',
1112                         cls: 'slider-button',
1113                         html: this.offtext
1114                     }
1115                 ]
1116             };
1117             // why are we validating the weights?
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 cfg.cls +=  ' ' + this.weight;
1120             }
1121             
1122             return cfg;
1123         }
1124         
1125         if (this.isClose) {
1126             cfg.cls += ' close';
1127             
1128             cfg["aria-hidden"] = true;
1129             
1130             cfg.html = "&times;";
1131             
1132             return cfg;
1133         }
1134              
1135         
1136         if (this.theme==='default') {
1137             cfg.cls = 'btn roo-button';
1138             
1139             //if (this.parentType != 'Navbar') {
1140             this.weight = this.weight.length ?  this.weight : 'default';
1141             //}
1142             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1143                 
1144                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146                 cfg.cls += ' btn-' + outline + weight;
1147                 if (this.weight == 'default') {
1148                     // BC
1149                     cfg.cls += ' btn-' + this.weight;
1150                 }
1151             }
1152         } else if (this.theme==='glow') {
1153             
1154             cfg.tag = 'a';
1155             cfg.cls = 'btn-glow roo-button';
1156             
1157             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1158                 
1159                 cfg.cls += ' ' + this.weight;
1160             }
1161         }
1162    
1163         
1164         if (this.inverse) {
1165             this.cls += ' inverse';
1166         }
1167         
1168         
1169         if (this.active || this.pressed === true) {
1170             cfg.cls += ' active';
1171         }
1172         
1173         if (this.disabled) {
1174             cfg.disabled = 'disabled';
1175         }
1176         
1177         if (this.items) {
1178             Roo.log('changing to ul' );
1179             cfg.tag = 'ul';
1180             this.glyphicon = 'caret';
1181             if (Roo.bootstrap.version == 4) {
1182                 this.fa = 'caret-down';
1183             }
1184             
1185         }
1186         
1187         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1188          
1189         //gsRoo.log(this.parentType);
1190         if (this.parentType === 'Navbar' && !this.parent().bar) {
1191             Roo.log('changing to li?');
1192             
1193             cfg.tag = 'li';
1194             
1195             cfg.cls = '';
1196             cfg.cn =  [{
1197                 tag : 'a',
1198                 cls : 'roo-button',
1199                 html : this.html,
1200                 href : this.href || '#'
1201             }];
1202             if (this.menu) {
1203                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1204                 cfg.cls += ' dropdown';
1205             }   
1206             
1207             delete cfg.html;
1208             
1209         }
1210         
1211        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1212         
1213         if (this.glyphicon) {
1214             cfg.html = ' ' + cfg.html;
1215             
1216             cfg.cn = [
1217                 {
1218                     tag: 'span',
1219                     cls: 'glyphicon glyphicon-' + this.glyphicon
1220                 }
1221             ];
1222         }
1223         if (this.fa) {
1224             cfg.html = ' ' + cfg.html;
1225             
1226             cfg.cn = [
1227                 {
1228                     tag: 'i',
1229                     cls: 'fa fas fa-' + this.fa
1230                 }
1231             ];
1232         }
1233         
1234         if (this.badge) {
1235             cfg.html += ' ';
1236             
1237             cfg.tag = 'a';
1238             
1239 //            cfg.cls='btn roo-button';
1240             
1241             cfg.href=this.href;
1242             
1243             var value = cfg.html;
1244             
1245             if(this.glyphicon){
1246                 value = {
1247                     tag: 'span',
1248                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1249                     html: this.html
1250                 };
1251             }
1252             if(this.fa){
1253                 value = {
1254                     tag: 'i',
1255                     cls: 'fa fas fa-' + this.fa,
1256                     html: this.html
1257                 };
1258             }
1259             
1260             var bw = this.badge_weight.length ? this.badge_weight :
1261                 (this.weight.length ? this.weight : 'secondary');
1262             bw = bw == 'default' ? 'secondary' : bw;
1263             
1264             cfg.cn = [
1265                 value,
1266                 {
1267                     tag: 'span',
1268                     cls: 'badge badge-' + bw,
1269                     html: this.badge
1270                 }
1271             ];
1272             
1273             cfg.html='';
1274         }
1275         
1276         if (this.menu) {
1277             cfg.cls += ' dropdown';
1278             cfg.html = typeof(cfg.html) != 'undefined' ?
1279                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280         }
1281         
1282         if (cfg.tag !== 'a' && this.href !== '') {
1283             throw "Tag must be a to set href.";
1284         } else if (this.href.length > 0) {
1285             cfg.href = this.href;
1286         }
1287         
1288         if(this.removeClass){
1289             cfg.cls = '';
1290         }
1291         
1292         if(this.target){
1293             cfg.target = this.target;
1294         }
1295         
1296         return cfg;
1297     },
1298     initEvents: function() {
1299        // Roo.log('init events?');
1300 //        Roo.log(this.el.dom);
1301         // add the menu...
1302         
1303         if (typeof (this.menu) != 'undefined') {
1304             this.menu.parentType = this.xtype;
1305             this.menu.triggerEl = this.el;
1306             this.addxtype(Roo.apply({}, this.menu));
1307         }
1308
1309
1310         if (this.el.hasClass('roo-button')) {
1311              this.el.on('click', this.onClick, this);
1312              this.el.on('dblclick', this.onDblClick, this);
1313         } else {
1314              this.el.select('.roo-button').on('click', this.onClick, this);
1315              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1316              
1317         }
1318         // why?
1319         if(this.removeClass){
1320             this.el.on('click', this.onClick, this);
1321         }
1322         
1323         if (this.group === true) {
1324              if (this.pressed === false || this.pressed === true) {
1325                 // nothing
1326             } else {
1327                 this.pressed = false;
1328                 this.setActive(this.pressed);
1329             }
1330             
1331         }
1332         
1333         this.el.enableDisplayMode();
1334         
1335     },
1336     onClick : function(e)
1337     {
1338         if (this.disabled) {
1339             return;
1340         }
1341         
1342         Roo.log('button on click ');
1343         if(this.href === '' || this.preventDefault){
1344             e.preventDefault();
1345         }
1346         
1347         if (this.group) {
1348             if (this.pressed) {
1349                 // do nothing -
1350                 return;
1351             }
1352             this.setActive(true);
1353             var pi = this.parent().items;
1354             for (var i = 0;i < pi.length;i++) {
1355                 if (this == pi[i]) {
1356                     continue;
1357                 }
1358                 if (pi[i].el.hasClass('roo-button')) {
1359                     pi[i].setActive(false);
1360                 }
1361             }
1362             this.fireEvent('click', this, e);            
1363             return;
1364         }
1365         
1366         if (this.pressed === true || this.pressed === false) {
1367             this.toggleActive(e);
1368         }
1369         
1370         
1371         this.fireEvent('click', this, e);
1372     },
1373     onDblClick: function(e)
1374     {
1375         if (this.disabled) {
1376             return;
1377         }
1378         if(this.preventDefault){
1379             e.preventDefault();
1380         }
1381         this.fireEvent('dblclick', this, e);
1382     },
1383     /**
1384      * Enables this button
1385      */
1386     enable : function()
1387     {
1388         this.disabled = false;
1389         this.el.removeClass('disabled');
1390         this.el.dom.removeAttribute("disabled");
1391     },
1392     
1393     /**
1394      * Disable this button
1395      */
1396     disable : function()
1397     {
1398         this.disabled = true;
1399         this.el.addClass('disabled');
1400         this.el.attr("disabled", "disabled")
1401     },
1402      /**
1403      * sets the active state on/off, 
1404      * @param {Boolean} state (optional) Force a particular state
1405      */
1406     setActive : function(v) {
1407         
1408         this.el[v ? 'addClass' : 'removeClass']('active');
1409         this.pressed = v;
1410     },
1411      /**
1412      * toggles the current active state 
1413      */
1414     toggleActive : function(e)
1415     {
1416         this.setActive(!this.pressed); // this modifies pressed...
1417         this.fireEvent('toggle', this, e, this.pressed);
1418     },
1419      /**
1420      * get the current active state
1421      * @return {boolean} true if it's active
1422      */
1423     isActive : function()
1424     {
1425         return this.el.hasClass('active');
1426     },
1427     /**
1428      * set the text of the first selected button
1429      */
1430     setText : function(str)
1431     {
1432         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433     },
1434     /**
1435      * get the text of the first selected button
1436      */
1437     getText : function()
1438     {
1439         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440     },
1441     
1442     setWeight : function(str)
1443     {
1444         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1446         this.weight = str;
1447         var outline = this.outline ? 'outline-' : '';
1448         if (str == 'default') {
1449             this.el.addClass('btn-default btn-outline-secondary');        
1450             return;
1451         }
1452         this.el.addClass('btn-' + outline + str);        
1453     }
1454     
1455     
1456 });
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1458
1459 Roo.bootstrap.Button.weights = [
1460     'default',
1461     'secondary' ,
1462     'primary',
1463     'success',
1464     'info',
1465     'warning',
1466     'danger',
1467     'link',
1468     'light',
1469     'dark'              
1470    
1471 ];/*
1472  * - LGPL
1473  *
1474  * column
1475  * 
1476  */
1477
1478 /**
1479  * @class Roo.bootstrap.Column
1480  * @extends Roo.bootstrap.Component
1481  * @children Roo.bootstrap.Component
1482  * Bootstrap Column class
1483  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491  *
1492  * 
1493  * @cfg {Boolean} hidden (true|false) hide the element
1494  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495  * @cfg {String} fa (ban|check|...) font awesome icon
1496  * @cfg {Number} fasize (1|2|....) font awsome size
1497
1498  * @cfg {String} icon (info-sign|check|...) glyphicon name
1499
1500  * @cfg {String} html content of column.
1501  * 
1502  * @constructor
1503  * Create a new Column
1504  * @param {Object} config The config object
1505  */
1506
1507 Roo.bootstrap.Column = function(config){
1508     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 };
1510
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1512     
1513     xs: false,
1514     sm: false,
1515     md: false,
1516     lg: false,
1517     xsoff: false,
1518     smoff: false,
1519     mdoff: false,
1520     lgoff: false,
1521     html: '',
1522     offset: 0,
1523     alert: false,
1524     fa: false,
1525     icon : false,
1526     hidden : false,
1527     fasize : 1,
1528     
1529     getAutoCreate : function(){
1530         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531         
1532         cfg = {
1533             tag: 'div',
1534             cls: 'column'
1535         };
1536         
1537         var settings=this;
1538         var sizes =   ['xs','sm','md','lg'];
1539         sizes.map(function(size ,ix){
1540             //Roo.log( size + ':' + settings[size]);
1541             
1542             if (settings[size+'off'] !== false) {
1543                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544             }
1545             
1546             if (settings[size] === false) {
1547                 return;
1548             }
1549             
1550             if (!settings[size]) { // 0 = hidden
1551                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1552                 // bootsrap4
1553                 for (var i = ix; i > -1; i--) {
1554                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1555                 }
1556                 
1557                 
1558                 return;
1559             }
1560             cfg.cls += ' col-' + size + '-' + settings[size] + (
1561                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562             );
1563             
1564         });
1565         
1566         if (this.hidden) {
1567             cfg.cls += ' hidden';
1568         }
1569         
1570         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571             cfg.cls +=' alert alert-' + this.alert;
1572         }
1573         
1574         
1575         if (this.html.length) {
1576             cfg.html = this.html;
1577         }
1578         if (this.fa) {
1579             var fasize = '';
1580             if (this.fasize > 1) {
1581                 fasize = ' fa-' + this.fasize + 'x';
1582             }
1583             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1584             
1585             
1586         }
1587         if (this.icon) {
1588             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1589         }
1590         
1591         return cfg;
1592     }
1593    
1594 });
1595
1596  
1597
1598  /*
1599  * - LGPL
1600  *
1601  * page container.
1602  * 
1603  */
1604
1605
1606 /**
1607  * @class Roo.bootstrap.Container
1608  * @extends Roo.bootstrap.Component
1609  * @children Roo.bootstrap.Component
1610  * @parent builder
1611  * Bootstrap Container class
1612  * @cfg {Boolean} jumbotron is it a jumbotron element
1613  * @cfg {String} html content of element
1614  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1616  * @cfg {String} header content of header (for panel)
1617  * @cfg {String} footer content of footer (for panel)
1618  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619  * @cfg {String} tag (header|aside|section) type of HTML tag.
1620  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621  * @cfg {String} fa font awesome icon
1622  * @cfg {String} icon (info-sign|check|...) glyphicon name
1623  * @cfg {Boolean} hidden (true|false) hide the element
1624  * @cfg {Boolean} expandable (true|false) default false
1625  * @cfg {Boolean} expanded (true|false) default true
1626  * @cfg {String} rheader contet on the right of header
1627  * @cfg {Boolean} clickable (true|false) default false
1628
1629  *     
1630  * @constructor
1631  * Create a new Container
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Container = function(config){
1636     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // raw events
1640          /**
1641          * @event expand
1642          * After the panel has been expand
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "expand" : true,
1647         /**
1648          * @event collapse
1649          * After the panel has been collapsed
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "collapse" : true,
1654         /**
1655          * @event click
1656          * When a element is chick
1657          * @param {Roo.bootstrap.Container} this
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1665     
1666     jumbotron : false,
1667     well: '',
1668     panel : '',
1669     header: '',
1670     footer : '',
1671     sticky: '',
1672     tag : false,
1673     alert : false,
1674     fa: false,
1675     icon : false,
1676     expandable : false,
1677     rheader : '',
1678     expanded : true,
1679     clickable: false,
1680   
1681      
1682     getChildContainer : function() {
1683         
1684         if(!this.el){
1685             return false;
1686         }
1687         
1688         if (this.panel.length) {
1689             return this.el.select('.panel-body',true).first();
1690         }
1691         
1692         return this.el;
1693     },
1694     
1695     
1696     getAutoCreate : function(){
1697         
1698         var cfg = {
1699             tag : this.tag || 'div',
1700             html : '',
1701             cls : ''
1702         };
1703         if (this.jumbotron) {
1704             cfg.cls = 'jumbotron';
1705         }
1706         
1707         
1708         
1709         // - this is applied by the parent..
1710         //if (this.cls) {
1711         //    cfg.cls = this.cls + '';
1712         //}
1713         
1714         if (this.sticky.length) {
1715             
1716             var bd = Roo.get(document.body);
1717             if (!bd.hasClass('bootstrap-sticky')) {
1718                 bd.addClass('bootstrap-sticky');
1719                 Roo.select('html',true).setStyle('height', '100%');
1720             }
1721              
1722             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1723         }
1724         
1725         
1726         if (this.well.length) {
1727             switch (this.well) {
1728                 case 'lg':
1729                 case 'sm':
1730                     cfg.cls +=' well well-' +this.well;
1731                     break;
1732                 default:
1733                     cfg.cls +=' well';
1734                     break;
1735             }
1736         }
1737         
1738         if (this.hidden) {
1739             cfg.cls += ' hidden';
1740         }
1741         
1742         
1743         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744             cfg.cls +=' alert alert-' + this.alert;
1745         }
1746         
1747         var body = cfg;
1748         
1749         if (this.panel.length) {
1750             cfg.cls += ' panel panel-' + this.panel;
1751             cfg.cn = [];
1752             if (this.header.length) {
1753                 
1754                 var h = [];
1755                 
1756                 if(this.expandable){
1757                     
1758                     cfg.cls = cfg.cls + ' expandable';
1759                     
1760                     h.push({
1761                         tag: 'i',
1762                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1763                     });
1764                     
1765                 }
1766                 
1767                 h.push(
1768                     {
1769                         tag: 'span',
1770                         cls : 'panel-title',
1771                         html : (this.expandable ? '&nbsp;' : '') + this.header
1772                     },
1773                     {
1774                         tag: 'span',
1775                         cls: 'panel-header-right',
1776                         html: this.rheader
1777                     }
1778                 );
1779                 
1780                 cfg.cn.push({
1781                     cls : 'panel-heading',
1782                     style : this.expandable ? 'cursor: pointer' : '',
1783                     cn : h
1784                 });
1785                 
1786             }
1787             
1788             body = false;
1789             cfg.cn.push({
1790                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1791                 html : this.html
1792             });
1793             
1794             
1795             if (this.footer.length) {
1796                 cfg.cn.push({
1797                     cls : 'panel-footer',
1798                     html : this.footer
1799                     
1800                 });
1801             }
1802             
1803         }
1804         
1805         if (body) {
1806             body.html = this.html || cfg.html;
1807             // prefix with the icons..
1808             if (this.fa) {
1809                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810             }
1811             if (this.icon) {
1812                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1813             }
1814             
1815             
1816         }
1817         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818             cfg.cls =  'container';
1819         }
1820         
1821         return cfg;
1822     },
1823     
1824     initEvents: function() 
1825     {
1826         if(this.expandable){
1827             var headerEl = this.headerEl();
1828         
1829             if(headerEl){
1830                 headerEl.on('click', this.onToggleClick, this);
1831             }
1832         }
1833         
1834         if(this.clickable){
1835             this.el.on('click', this.onClick, this);
1836         }
1837         
1838     },
1839     
1840     onToggleClick : function()
1841     {
1842         var headerEl = this.headerEl();
1843         
1844         if(!headerEl){
1845             return;
1846         }
1847         
1848         if(this.expanded){
1849             this.collapse();
1850             return;
1851         }
1852         
1853         this.expand();
1854     },
1855     
1856     expand : function()
1857     {
1858         if(this.fireEvent('expand', this)) {
1859             
1860             this.expanded = true;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1863             
1864             this.el.select('.panel-body',true).first().removeClass('hide');
1865             
1866             var toggleEl = this.toggleEl();
1867
1868             if(!toggleEl){
1869                 return;
1870             }
1871
1872             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1873         }
1874         
1875     },
1876     
1877     collapse : function()
1878     {
1879         if(this.fireEvent('collapse', this)) {
1880             
1881             this.expanded = false;
1882             
1883             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884             this.el.select('.panel-body',true).first().addClass('hide');
1885         
1886             var toggleEl = this.toggleEl();
1887
1888             if(!toggleEl){
1889                 return;
1890             }
1891
1892             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1893         }
1894     },
1895     
1896     toggleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-heading .fa',true).first();
1903     },
1904     
1905     headerEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading',true).first()
1912     },
1913     
1914     bodyEl : function()
1915     {
1916         if(!this.el || !this.panel.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-body',true).first()
1921     },
1922     
1923     titleEl : function()
1924     {
1925         if(!this.el || !this.panel.length || !this.header.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-title',true).first();
1930     },
1931     
1932     setTitle : function(v)
1933     {
1934         var titleEl = this.titleEl();
1935         
1936         if(!titleEl){
1937             return;
1938         }
1939         
1940         titleEl.dom.innerHTML = v;
1941     },
1942     
1943     getTitle : function()
1944     {
1945         
1946         var titleEl = this.titleEl();
1947         
1948         if(!titleEl){
1949             return '';
1950         }
1951         
1952         return titleEl.dom.innerHTML;
1953     },
1954     
1955     setRightTitle : function(v)
1956     {
1957         var t = this.el.select('.panel-header-right',true).first();
1958         
1959         if(!t){
1960             return;
1961         }
1962         
1963         t.dom.innerHTML = v;
1964     },
1965     
1966     onClick : function(e)
1967     {
1968         e.preventDefault();
1969         
1970         this.fireEvent('click', this, e);
1971     }
1972 });
1973
1974  /**
1975  * @class Roo.bootstrap.Card
1976  * @extends Roo.bootstrap.Component
1977  * @children Roo.bootstrap.Component
1978  * @licence LGPL
1979  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * @parent Roo.bootstrap.Card
2781  * @children Roo.bootstrap.Component
2782  * Bootstrap CardHeader class
2783  * @constructor
2784  * Create a new Card Header - that you can embed children into
2785  * @param {Object} config The config object
2786  */
2787
2788 Roo.bootstrap.CardHeader = function(config){
2789     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 };
2791
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2793     
2794     
2795     container_method : 'getCardHeader' 
2796     
2797      
2798     
2799     
2800    
2801 });
2802
2803  
2804
2805  /*
2806  * - LGPL
2807  *
2808  * Card footer - holder for the card footer elements.
2809  * 
2810  */
2811
2812 /**
2813  * @class Roo.bootstrap.CardFooter
2814  * @extends Roo.bootstrap.Element
2815  * @parent Roo.bootstrap.Card
2816  * @children Roo.bootstrap.Component
2817  * Bootstrap CardFooter class
2818  * 
2819  * @constructor
2820  * Create a new Card Footer - that you can embed children into
2821  * @param {Object} config The config object
2822  */
2823
2824 Roo.bootstrap.CardFooter = function(config){
2825     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 };
2827
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2829     
2830     
2831     container_method : 'getCardFooter' 
2832     
2833      
2834     
2835     
2836    
2837 });
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * Card header - holder for the card header elements.
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.CardImageTop
2850  * @extends Roo.bootstrap.Element
2851  * @parent Roo.bootstrap.Card
2852  * @children Roo.bootstrap.Component
2853  * Bootstrap CardImageTop class
2854  * 
2855  * @constructor
2856  * Create a new Card Image Top container
2857  * @param {Object} config The config object
2858  */
2859
2860 Roo.bootstrap.CardImageTop = function(config){
2861     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 };
2863
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2865     
2866    
2867     container_method : 'getCardImageTop' 
2868     
2869      
2870     
2871    
2872 });
2873
2874  
2875
2876  
2877 /*
2878 * Licence: LGPL
2879 */
2880
2881 /**
2882  * @class Roo.bootstrap.ButtonUploader
2883  * @extends Roo.bootstrap.Button
2884  * Bootstrap Button Uploader class - it's a button which when you add files to it
2885  *
2886  * 
2887  * @cfg {Number} errorTimeout default 3000
2888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2889  * @cfg {Array}  html The button text.
2890  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2891  *
2892  * @constructor
2893  * Create a new CardUploader
2894  * @param {Object} config The config object
2895  */
2896
2897 Roo.bootstrap.ButtonUploader = function(config){
2898     
2899  
2900     
2901     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2902     
2903      
2904      this.addEvents({
2905          // raw events
2906         /**
2907          * @event beforeselect
2908          * When button is pressed, before show upload files dialog is shown
2909          * @param {Roo.bootstrap.UploaderButton} this
2910          *
2911          */
2912         'beforeselect' : true,
2913          /**
2914          * @event fired when files have been selected, 
2915          * When a the download link is clicked
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          * @param {Array} Array of files that have been uploaded
2918          */
2919         'uploaded' : true
2920         
2921     });
2922 };
2923  
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2925     
2926      
2927     errorTimeout : 3000,
2928      
2929     images : false,
2930    
2931     fileCollection : false,
2932     allowBlank : true,
2933     
2934     multiple : true,
2935     
2936     getAutoCreate : function()
2937     {
2938        
2939         
2940         return  {
2941             cls :'div' ,
2942             cn : [
2943                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2944             ]
2945         };
2946            
2947          
2948     },
2949      
2950    
2951     initEvents : function()
2952     {
2953         
2954         Roo.bootstrap.Button.prototype.initEvents.call(this);
2955         
2956         
2957         
2958         
2959         
2960         this.urlAPI = (window.createObjectURL && window) || 
2961                                 (window.URL && URL.revokeObjectURL && URL) || 
2962                                 (window.webkitURL && webkitURL);
2963                         
2964         var im = {
2965             tag: 'input',
2966             type : 'file',
2967             cls : 'd-none  roo-card-upload-selector' 
2968           
2969         };
2970         if (this.multiple) {
2971             im.multiple = 'multiple';
2972         }
2973         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2974        
2975         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2976         
2977         this.selectorEl.on('change', this.onFileSelected, this);
2978          
2979          
2980        
2981     },
2982     
2983    
2984     onClick : function(e)
2985     {
2986         e.preventDefault();
2987         
2988         if ( this.fireEvent('beforeselect', this) === false) {
2989             return;
2990         }
2991          
2992         this.selectorEl.dom.click();
2993          
2994     },
2995     
2996     onFileSelected : function(e)
2997     {
2998         e.preventDefault();
2999         
3000         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3001             return;
3002         }
3003         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004         this.selectorEl.dom.value  = '';// hopefully reset..
3005         
3006         this.fireEvent('uploaded', this,  files );
3007         
3008     },
3009     
3010        
3011    
3012     
3013     /**
3014      * addCard - add an Attachment to the uploader
3015      * @param data - the data about the image to upload
3016      *
3017      * {
3018           id : 123
3019           title : "Title of file",
3020           is_uploaded : false,
3021           src : "http://.....",
3022           srcfile : { the File upload object },
3023           mimetype : file.type,
3024           preview : false,
3025           is_deleted : 0
3026           .. any other data...
3027         }
3028      *
3029      * 
3030     */
3031      
3032     reset: function()
3033     {
3034          
3035          this.selectorEl
3036     } 
3037     
3038     
3039     
3040     
3041 });
3042  /*
3043  * - LGPL
3044  *
3045  * image
3046  * 
3047  */
3048
3049
3050 /**
3051  * @class Roo.bootstrap.Img
3052  * @extends Roo.bootstrap.Component
3053  * Bootstrap Img class
3054  * @cfg {Boolean} imgResponsive false | true
3055  * @cfg {String} border rounded | circle | thumbnail
3056  * @cfg {String} src image source
3057  * @cfg {String} alt image alternative text
3058  * @cfg {String} href a tag href
3059  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060  * @cfg {String} xsUrl xs image source
3061  * @cfg {String} smUrl sm image source
3062  * @cfg {String} mdUrl md image source
3063  * @cfg {String} lgUrl lg image source
3064  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3065  * 
3066  * @constructor
3067  * Create a new Input
3068  * @param {Object} config The config object
3069  */
3070
3071 Roo.bootstrap.Img = function(config){
3072     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3073     
3074     this.addEvents({
3075         // img events
3076         /**
3077          * @event click
3078          * The img click event for the img.
3079          * @param {Roo.EventObject} e
3080          */
3081         "click" : true,
3082         /**
3083          * @event load
3084          * The when any image loads
3085          * @param {Roo.EventObject} e
3086          */
3087         "load" : true
3088     });
3089 };
3090
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3092     
3093     imgResponsive: true,
3094     border: '',
3095     src: 'about:blank',
3096     href: false,
3097     target: false,
3098     xsUrl: '',
3099     smUrl: '',
3100     mdUrl: '',
3101     lgUrl: '',
3102     backgroundContain : false,
3103
3104     getAutoCreate : function()
3105     {   
3106         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107             return this.createSingleImg();
3108         }
3109         
3110         var cfg = {
3111             tag: 'div',
3112             cls: 'roo-image-responsive-group',
3113             cn: []
3114         };
3115         var _this = this;
3116         
3117         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3118             
3119             if(!_this[size + 'Url']){
3120                 return;
3121             }
3122             
3123             var img = {
3124                 tag: 'img',
3125                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126                 html: _this.html || cfg.html,
3127                 src: _this[size + 'Url']
3128             };
3129             
3130             img.cls += ' roo-image-responsive-' + size;
3131             
3132             var s = ['xs', 'sm', 'md', 'lg'];
3133             
3134             s.splice(s.indexOf(size), 1);
3135             
3136             Roo.each(s, function(ss){
3137                 img.cls += ' hidden-' + ss;
3138             });
3139             
3140             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141                 cfg.cls += ' img-' + _this.border;
3142             }
3143             
3144             if(_this.alt){
3145                 cfg.alt = _this.alt;
3146             }
3147             
3148             if(_this.href){
3149                 var a = {
3150                     tag: 'a',
3151                     href: _this.href,
3152                     cn: [
3153                         img
3154                     ]
3155                 };
3156
3157                 if(this.target){
3158                     a.target = _this.target;
3159                 }
3160             }
3161             
3162             cfg.cn.push((_this.href) ? a : img);
3163             
3164         });
3165         
3166         return cfg;
3167     },
3168     
3169     createSingleImg : function()
3170     {
3171         var cfg = {
3172             tag: 'img',
3173             cls: (this.imgResponsive) ? 'img-responsive' : '',
3174             html : null,
3175             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3176         };
3177         
3178         if (this.backgroundContain) {
3179             cfg.cls += ' background-contain';
3180         }
3181         
3182         cfg.html = this.html || cfg.html;
3183         
3184         if (this.backgroundContain) {
3185             cfg.style="background-image: url(" + this.src + ')';
3186         } else {
3187             cfg.src = this.src || cfg.src;
3188         }
3189         
3190         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191             cfg.cls += ' img-' + this.border;
3192         }
3193         
3194         if(this.alt){
3195             cfg.alt = this.alt;
3196         }
3197         
3198         if(this.href){
3199             var a = {
3200                 tag: 'a',
3201                 href: this.href,
3202                 cn: [
3203                     cfg
3204                 ]
3205             };
3206             
3207             if(this.target){
3208                 a.target = this.target;
3209             }
3210             
3211         }
3212         
3213         return (this.href) ? a : cfg;
3214     },
3215     
3216     initEvents: function() 
3217     {
3218         if(!this.href){
3219             this.el.on('click', this.onClick, this);
3220         }
3221         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222             this.el.on('load', this.onImageLoad, this);
3223         } else {
3224             // not sure if this works.. not tested
3225             this.el.select('img', true).on('load', this.onImageLoad, this);
3226         }
3227         
3228     },
3229     
3230     onClick : function(e)
3231     {
3232         Roo.log('img onclick');
3233         this.fireEvent('click', this, e);
3234     },
3235     onImageLoad: function(e)
3236     {
3237         Roo.log('img load');
3238         this.fireEvent('load', this, e);
3239     },
3240     
3241     /**
3242      * Sets the url of the image - used to update it
3243      * @param {String} url the url of the image
3244      */
3245     
3246     setSrc : function(url)
3247     {
3248         this.src =  url;
3249         
3250         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251             if (this.backgroundContain) {
3252                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3253             } else {
3254                 this.el.dom.src =  url;
3255             }
3256             return;
3257         }
3258         
3259         this.el.select('img', true).first().dom.src =  url;
3260     }
3261     
3262     
3263    
3264 });
3265
3266  /*
3267  * - LGPL
3268  *
3269  * image
3270  * 
3271  */
3272
3273
3274 /**
3275  * @class Roo.bootstrap.Link
3276  * @extends Roo.bootstrap.Component
3277  * @children Roo.bootstrap.Component
3278  * Bootstrap Link Class (eg. '<a href>')
3279  
3280  * @cfg {String} alt image alternative text
3281  * @cfg {String} href a tag href
3282  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283  * @cfg {String} html the content of the link.
3284  * @cfg {String} anchor name for the anchor link
3285  * @cfg {String} fa - favicon
3286
3287  * @cfg {Boolean} preventDefault (true | false) default false
3288
3289  * 
3290  * @constructor
3291  * Create a new Input
3292  * @param {Object} config The config object
3293  */
3294
3295 Roo.bootstrap.Link = function(config){
3296     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3297     
3298     this.addEvents({
3299         // img events
3300         /**
3301          * @event click
3302          * The img click event for the img.
3303          * @param {Roo.EventObject} e
3304          */
3305         "click" : true
3306     });
3307 };
3308
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3310     
3311     href: false,
3312     target: false,
3313     preventDefault: false,
3314     anchor : false,
3315     alt : false,
3316     fa: false,
3317
3318
3319     getAutoCreate : function()
3320     {
3321         var html = this.html || '';
3322         
3323         if (this.fa !== false) {
3324             html = '<i class="fa fa-' + this.fa + '"></i>';
3325         }
3326         var cfg = {
3327             tag: 'a'
3328         };
3329         // anchor's do not require html/href...
3330         if (this.anchor === false) {
3331             cfg.html = html;
3332             cfg.href = this.href || '#';
3333         } else {
3334             cfg.name = this.anchor;
3335             if (this.html !== false || this.fa !== false) {
3336                 cfg.html = html;
3337             }
3338             if (this.href !== false) {
3339                 cfg.href = this.href;
3340             }
3341         }
3342         
3343         if(this.alt !== false){
3344             cfg.alt = this.alt;
3345         }
3346         
3347         
3348         if(this.target !== false) {
3349             cfg.target = this.target;
3350         }
3351         
3352         return cfg;
3353     },
3354     
3355     initEvents: function() {
3356         
3357         if(!this.href || this.preventDefault){
3358             this.el.on('click', this.onClick, this);
3359         }
3360     },
3361     
3362     onClick : function(e)
3363     {
3364         if(this.preventDefault){
3365             e.preventDefault();
3366         }
3367         //Roo.log('img onclick');
3368         this.fireEvent('click', this, e);
3369     }
3370    
3371 });
3372
3373  /*
3374  * - LGPL
3375  *
3376  * header
3377  * 
3378  */
3379
3380 /**
3381  * @class Roo.bootstrap.Header
3382  * @extends Roo.bootstrap.Component
3383  * @children Roo.bootstrap.Component
3384  * Bootstrap Header class
3385  *
3386  * 
3387  * @cfg {String} html content of header
3388  * @cfg {Number} level (1|2|3|4|5|6) default 1
3389  * 
3390  * @constructor
3391  * Create a new Header
3392  * @param {Object} config The config object
3393  */
3394
3395
3396 Roo.bootstrap.Header  = function(config){
3397     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3398 };
3399
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3401     
3402     //href : false,
3403     html : false,
3404     level : 1,
3405     
3406     
3407     
3408     getAutoCreate : function(){
3409         
3410         
3411         
3412         var cfg = {
3413             tag: 'h' + (1 *this.level),
3414             html: this.html || ''
3415         } ;
3416         
3417         return cfg;
3418     }
3419    
3420 });
3421
3422  
3423
3424  /**
3425  * @class Roo.bootstrap.MenuMgr
3426  * @licence LGPL
3427  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3428  * @static
3429  */
3430 Roo.bootstrap.menu.Manager = function(){
3431    var menus, active, groups = {}, attached = false, lastShow = new Date();
3432
3433    // private - called when first menu is created
3434    function init(){
3435        menus = {};
3436        active = new Roo.util.MixedCollection();
3437        Roo.get(document).addKeyListener(27, function(){
3438            if(active.length > 0){
3439                hideAll();
3440            }
3441        });
3442    }
3443
3444    // private
3445    function hideAll(){
3446        if(active && active.length > 0){
3447            var c = active.clone();
3448            c.each(function(m){
3449                m.hide();
3450            });
3451        }
3452    }
3453
3454    // private
3455    function onHide(m){
3456        active.remove(m);
3457        if(active.length < 1){
3458            Roo.get(document).un("mouseup", onMouseDown);
3459             
3460            attached = false;
3461        }
3462    }
3463
3464    // private
3465    function onShow(m){
3466        var last = active.last();
3467        lastShow = new Date();
3468        active.add(m);
3469        if(!attached){
3470           Roo.get(document).on("mouseup", onMouseDown);
3471            
3472            attached = true;
3473        }
3474        if(m.parentMenu){
3475           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3476           m.parentMenu.activeChild = m;
3477        }else if(last && last.isVisible()){
3478           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3479        }
3480    }
3481
3482    // private
3483    function onBeforeHide(m){
3484        if(m.activeChild){
3485            m.activeChild.hide();
3486        }
3487        if(m.autoHideTimer){
3488            clearTimeout(m.autoHideTimer);
3489            delete m.autoHideTimer;
3490        }
3491    }
3492
3493    // private
3494    function onBeforeShow(m){
3495        var pm = m.parentMenu;
3496        if(!pm && !m.allowOtherMenus){
3497            hideAll();
3498        }else if(pm && pm.activeChild && active != m){
3499            pm.activeChild.hide();
3500        }
3501    }
3502
3503    // private this should really trigger on mouseup..
3504    function onMouseDown(e){
3505         Roo.log("on Mouse Up");
3506         
3507         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3508             Roo.log("MenuManager hideAll");
3509             hideAll();
3510             e.stopEvent();
3511         }
3512         
3513         
3514    }
3515
3516    // private
3517    function onBeforeCheck(mi, state){
3518        if(state){
3519            var g = groups[mi.group];
3520            for(var i = 0, l = g.length; i < l; i++){
3521                if(g[i] != mi){
3522                    g[i].setChecked(false);
3523                }
3524            }
3525        }
3526    }
3527
3528    return {
3529
3530        /**
3531         * Hides all menus that are currently visible
3532         */
3533        hideAll : function(){
3534             hideAll();  
3535        },
3536
3537        // private
3538        register : function(menu){
3539            if(!menus){
3540                init();
3541            }
3542            menus[menu.id] = menu;
3543            menu.on("beforehide", onBeforeHide);
3544            menu.on("hide", onHide);
3545            menu.on("beforeshow", onBeforeShow);
3546            menu.on("show", onShow);
3547            var g = menu.group;
3548            if(g && menu.events["checkchange"]){
3549                if(!groups[g]){
3550                    groups[g] = [];
3551                }
3552                groups[g].push(menu);
3553                menu.on("checkchange", onCheck);
3554            }
3555        },
3556
3557         /**
3558          * Returns a {@link Roo.menu.Menu} object
3559          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3560          * be used to generate and return a new Menu instance.
3561          */
3562        get : function(menu){
3563            if(typeof menu == "string"){ // menu id
3564                return menus[menu];
3565            }else if(menu.events){  // menu instance
3566                return menu;
3567            }
3568            /*else if(typeof menu.length == 'number'){ // array of menu items?
3569                return new Roo.bootstrap.Menu({items:menu});
3570            }else{ // otherwise, must be a config
3571                return new Roo.bootstrap.Menu(menu);
3572            }
3573            */
3574            return false;
3575        },
3576
3577        // private
3578        unregister : function(menu){
3579            delete menus[menu.id];
3580            menu.un("beforehide", onBeforeHide);
3581            menu.un("hide", onHide);
3582            menu.un("beforeshow", onBeforeShow);
3583            menu.un("show", onShow);
3584            var g = menu.group;
3585            if(g && menu.events["checkchange"]){
3586                groups[g].remove(menu);
3587                menu.un("checkchange", onCheck);
3588            }
3589        },
3590
3591        // private
3592        registerCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                if(!groups[g]){
3596                    groups[g] = [];
3597                }
3598                groups[g].push(menuItem);
3599                menuItem.on("beforecheckchange", onBeforeCheck);
3600            }
3601        },
3602
3603        // private
3604        unregisterCheckable : function(menuItem){
3605            var g = menuItem.group;
3606            if(g){
3607                groups[g].remove(menuItem);
3608                menuItem.un("beforecheckchange", onBeforeCheck);
3609            }
3610        }
3611    };
3612 }(); 
3613 /**
3614  * @class Roo.bootstrap.menu.Menu
3615  * @extends Roo.bootstrap.Component
3616  * @licence LGPL
3617  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3618  * @parent none
3619  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3620  * 
3621  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3623  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3624  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3625 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3626 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3627  
3628  * @constructor
3629  * Create a new Menu
3630  * @param {Object} config The config objectQ
3631  */
3632
3633
3634 Roo.bootstrap.menu.Menu = function(config){
3635     
3636     if (config.type == 'treeview') {
3637         // normally menu's are drawn attached to the document to handle layering etc..
3638         // however treeview (used by the docs menu is drawn into the parent element)
3639         this.container_method = 'getChildContainer'; 
3640     }
3641     
3642     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643     if (this.registerMenu && this.type != 'treeview')  {
3644         Roo.bootstrap.menu.Manager.register(this);
3645     }
3646     
3647     
3648     this.addEvents({
3649         /**
3650          * @event beforeshow
3651          * Fires before this menu is displayed (return false to block)
3652          * @param {Roo.menu.Menu} this
3653          */
3654         beforeshow : true,
3655         /**
3656          * @event beforehide
3657          * Fires before this menu is hidden (return false to block)
3658          * @param {Roo.menu.Menu} this
3659          */
3660         beforehide : true,
3661         /**
3662          * @event show
3663          * Fires after this menu is displayed
3664          * @param {Roo.menu.Menu} this
3665          */
3666         show : true,
3667         /**
3668          * @event hide
3669          * Fires after this menu is hidden
3670          * @param {Roo.menu.Menu} this
3671          */
3672         hide : true,
3673         /**
3674          * @event click
3675          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676          * @param {Roo.menu.Menu} this
3677          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678          * @param {Roo.EventObject} e
3679          */
3680         click : true,
3681         /**
3682          * @event mouseover
3683          * Fires when the mouse is hovering over this menu
3684          * @param {Roo.menu.Menu} this
3685          * @param {Roo.EventObject} e
3686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3687          */
3688         mouseover : true,
3689         /**
3690          * @event mouseout
3691          * Fires when the mouse exits this menu
3692          * @param {Roo.menu.Menu} this
3693          * @param {Roo.EventObject} e
3694          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3695          */
3696         mouseout : true,
3697         /**
3698          * @event itemclick
3699          * Fires when a menu item contained in this menu is clicked
3700          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701          * @param {Roo.EventObject} e
3702          */
3703         itemclick: true
3704     });
3705     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3706 };
3707
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3709     
3710    /// html : false,
3711    
3712     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3713     type: false,
3714     /**
3715      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3716      */
3717     registerMenu : true,
3718     
3719     menuItems :false, // stores the menu items..
3720     
3721     hidden:true,
3722         
3723     parentMenu : false,
3724     
3725     stopEvent : true,
3726     
3727     isLink : false,
3728     
3729     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3730     
3731     hideTrigger : false,
3732     
3733     align : 'tl-bl?',
3734     
3735     
3736     getChildContainer : function() {
3737         return this.el;  
3738     },
3739     
3740     getAutoCreate : function(){
3741          
3742         //if (['right'].indexOf(this.align)!==-1) {
3743         //    cfg.cn[1].cls += ' pull-right'
3744         //}
3745          
3746         var cfg = {
3747             tag : 'ul',
3748             cls : 'dropdown-menu shadow' ,
3749             style : 'z-index:1000'
3750             
3751         };
3752         
3753         if (this.type === 'submenu') {
3754             cfg.cls = 'submenu active';
3755         }
3756         if (this.type === 'treeview') {
3757             cfg.cls = 'treeview-menu';
3758         }
3759         
3760         return cfg;
3761     },
3762     initEvents : function() {
3763         
3764        // Roo.log("ADD event");
3765        // Roo.log(this.triggerEl.dom);
3766         if (this.triggerEl) {
3767             
3768             this.triggerEl.on('click', this.onTriggerClick, this);
3769             
3770             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3771             
3772             if (!this.hideTrigger) {
3773                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774                     // dropdown toggle on the 'a' in BS4?
3775                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3776                 } else {
3777                     this.triggerEl.addClass('dropdown-toggle');
3778                 }
3779             }
3780         }
3781         
3782         if (Roo.isTouch) {
3783             this.el.on('touchstart'  , this.onTouch, this);
3784         }
3785         this.el.on('click' , this.onClick, this);
3786
3787         this.el.on("mouseover", this.onMouseOver, this);
3788         this.el.on("mouseout", this.onMouseOut, this);
3789         
3790     },
3791     
3792     findTargetItem : function(e)
3793     {
3794         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3795         if(!t){
3796             return false;
3797         }
3798         //Roo.log(t);         Roo.log(t.id);
3799         if(t && t.id){
3800             //Roo.log(this.menuitems);
3801             return this.menuitems.get(t.id);
3802             
3803             //return this.items.get(t.menuItemId);
3804         }
3805         
3806         return false;
3807     },
3808     
3809     onTouch : function(e) 
3810     {
3811         Roo.log("menu.onTouch");
3812         //e.stopEvent(); this make the user popdown broken
3813         this.onClick(e);
3814     },
3815     
3816     onClick : function(e)
3817     {
3818         Roo.log("menu.onClick");
3819         
3820         var t = this.findTargetItem(e);
3821         if(!t || t.isContainer){
3822             return;
3823         }
3824         Roo.log(e);
3825         /*
3826         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3827             if(t == this.activeItem && t.shouldDeactivate(e)){
3828                 this.activeItem.deactivate();
3829                 delete this.activeItem;
3830                 return;
3831             }
3832             if(t.canActivate){
3833                 this.setActiveItem(t, true);
3834             }
3835             return;
3836             
3837             
3838         }
3839         */
3840        
3841         Roo.log('pass click event');
3842         
3843         t.onClick(e);
3844         
3845         this.fireEvent("click", this, t, e);
3846         
3847         var _this = this;
3848         
3849         if(!t.href.length || t.href == '#'){
3850             (function() { _this.hide(); }).defer(100);
3851         }
3852         
3853     },
3854     
3855     onMouseOver : function(e){
3856         var t  = this.findTargetItem(e);
3857         //Roo.log(t);
3858         //if(t){
3859         //    if(t.canActivate && !t.disabled){
3860         //        this.setActiveItem(t, true);
3861         //    }
3862         //}
3863         
3864         this.fireEvent("mouseover", this, e, t);
3865     },
3866     isVisible : function(){
3867         return !this.hidden;
3868     },
3869     onMouseOut : function(e){
3870         var t  = this.findTargetItem(e);
3871         
3872         //if(t ){
3873         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3874         //        this.activeItem.deactivate();
3875         //        delete this.activeItem;
3876         //    }
3877         //}
3878         this.fireEvent("mouseout", this, e, t);
3879     },
3880     
3881     
3882     /**
3883      * Displays this menu relative to another element
3884      * @param {String/HTMLElement/Roo.Element} element The element to align to
3885      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886      * the element (defaults to this.defaultAlign)
3887      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3888      */
3889     show : function(el, pos, parentMenu)
3890     {
3891         if (false === this.fireEvent("beforeshow", this)) {
3892             Roo.log("show canceled");
3893             return;
3894         }
3895         this.parentMenu = parentMenu;
3896         if(!this.el){
3897             this.render();
3898         }
3899         this.el.addClass('show'); // show otherwise we do not know how big we are..
3900          
3901         var xy = this.el.getAlignToXY(el, pos);
3902         
3903         // bl-tl << left align  below
3904         // tl-bl << left align 
3905         
3906         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907             // if it goes to far to the right.. -> align left.
3908             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3909         }
3910         if(xy[0] < 0){
3911             // was left align - go right?
3912             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3913         }
3914         
3915         // goes down the bottom
3916         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3917            xy[1]  < 0 ){
3918             var a = this.align.replace('?', '').split('-');
3919             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3920             
3921         }
3922         
3923         this.showAt(  xy , parentMenu, false);
3924     },
3925      /**
3926      * Displays this menu at a specific xy position
3927      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3929      */
3930     showAt : function(xy, parentMenu, /* private: */_e){
3931         this.parentMenu = parentMenu;
3932         if(!this.el){
3933             this.render();
3934         }
3935         if(_e !== false){
3936             this.fireEvent("beforeshow", this);
3937             //xy = this.el.adjustForConstraints(xy);
3938         }
3939         
3940         //this.el.show();
3941         this.hideMenuItems();
3942         this.hidden = false;
3943         if (this.triggerEl) {
3944             this.triggerEl.addClass('open');
3945         }
3946         
3947         this.el.addClass('show');
3948         
3949         
3950         
3951         // reassign x when hitting right
3952         
3953         // reassign y when hitting bottom
3954         
3955         // but the list may align on trigger left or trigger top... should it be a properity?
3956         
3957         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3958             this.el.setXY(xy);
3959         }
3960         
3961         this.focus();
3962         this.fireEvent("show", this);
3963     },
3964     
3965     focus : function(){
3966         return;
3967         if(!this.hidden){
3968             this.doFocus.defer(50, this);
3969         }
3970     },
3971
3972     doFocus : function(){
3973         if(!this.hidden){
3974             this.focusEl.focus();
3975         }
3976     },
3977
3978     /**
3979      * Hides this menu and optionally all parent menus
3980      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3981      */
3982     hide : function(deep)
3983     {
3984         if (false === this.fireEvent("beforehide", this)) {
3985             Roo.log("hide canceled");
3986             return;
3987         }
3988         this.hideMenuItems();
3989         if(this.el && this.isVisible()){
3990            
3991             if(this.activeItem){
3992                 this.activeItem.deactivate();
3993                 this.activeItem = null;
3994             }
3995             if (this.triggerEl) {
3996                 this.triggerEl.removeClass('open');
3997             }
3998             
3999             this.el.removeClass('show');
4000             this.hidden = true;
4001             this.fireEvent("hide", this);
4002         }
4003         if(deep === true && this.parentMenu){
4004             this.parentMenu.hide(true);
4005         }
4006     },
4007     
4008     onTriggerClick : function(e)
4009     {
4010         Roo.log('trigger click');
4011         
4012         var target = e.getTarget();
4013         
4014         Roo.log(target.nodeName.toLowerCase());
4015         
4016         if(target.nodeName.toLowerCase() === 'i'){
4017             e.preventDefault();
4018         }
4019         
4020     },
4021     
4022     onTriggerPress  : function(e)
4023     {
4024         Roo.log('trigger press');
4025         //Roo.log(e.getTarget());
4026        // Roo.log(this.triggerEl.dom);
4027        
4028         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029         var pel = Roo.get(e.getTarget());
4030         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031             Roo.log('is treeview or dropdown?');
4032             return;
4033         }
4034         
4035         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4036             return;
4037         }
4038         
4039         if (this.isVisible()) {
4040             Roo.log('hide');
4041             this.hide();
4042         } else {
4043             Roo.log('show');
4044             
4045             this.show(this.triggerEl, this.align, false);
4046         }
4047         
4048         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4049             e.stopEvent();
4050         }
4051         
4052     },
4053        
4054     
4055     hideMenuItems : function()
4056     {
4057         Roo.log("hide Menu Items");
4058         if (!this.el) { 
4059             return;
4060         }
4061         
4062         this.el.select('.open',true).each(function(aa) {
4063             
4064             aa.removeClass('open');
4065          
4066         });
4067     },
4068     addxtypeChild : function (tree, cntr) {
4069         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4070           
4071         this.menuitems.add(comp);
4072         return comp;
4073
4074     },
4075     getEl : function()
4076     {
4077         Roo.log(this.el);
4078         return this.el;
4079     },
4080     
4081     clear : function()
4082     {
4083         this.getEl().dom.innerHTML = '';
4084         this.menuitems.clear();
4085     }
4086 });
4087
4088  
4089  /**
4090  * @class Roo.bootstrap.menu.Item
4091  * @extends Roo.bootstrap.Component
4092  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093  * @parent Roo.bootstrap.menu.Menu
4094  * @licence LGPL
4095  * Bootstrap MenuItem class
4096  * 
4097  * @cfg {String} html the menu label
4098  * @cfg {String} href the link
4099  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4100  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4101  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4102  * @cfg {String} fa favicon to show on left of menu item.
4103  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4104  * 
4105  * 
4106  * @constructor
4107  * Create a new MenuItem
4108  * @param {Object} config The config object
4109  */
4110
4111
4112 Roo.bootstrap.menu.Item = function(config){
4113     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4114     this.addEvents({
4115         // raw events
4116         /**
4117          * @event click
4118          * The raw click event for the entire grid.
4119          * @param {Roo.bootstrap.menu.Item} this
4120          * @param {Roo.EventObject} e
4121          */
4122         "click" : true
4123     });
4124 };
4125
4126 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4127     
4128     href : false,
4129     html : false,
4130     preventDefault: false,
4131     isContainer : false,
4132     active : false,
4133     fa: false,
4134     
4135     getAutoCreate : function(){
4136         
4137         if(this.isContainer){
4138             return {
4139                 tag: 'li',
4140                 cls: 'dropdown-menu-item '
4141             };
4142         }
4143         var ctag = {
4144             tag: 'span',
4145             html: 'Link'
4146         };
4147         
4148         var anc = {
4149             tag : 'a',
4150             cls : 'dropdown-item',
4151             href : '#',
4152             cn : [  ]
4153         };
4154         
4155         if (this.fa !== false) {
4156             anc.cn.push({
4157                 tag : 'i',
4158                 cls : 'fa fa-' + this.fa
4159             });
4160         }
4161         
4162         anc.cn.push(ctag);
4163         
4164         
4165         var cfg= {
4166             tag: 'li',
4167             cls: 'dropdown-menu-item',
4168             cn: [ anc ]
4169         };
4170         if (this.parent().type == 'treeview') {
4171             cfg.cls = 'treeview-menu';
4172         }
4173         if (this.active) {
4174             cfg.cls += ' active';
4175         }
4176         
4177         
4178         
4179         anc.href = this.href || cfg.cn[0].href ;
4180         ctag.html = this.html || cfg.cn[0].html ;
4181         return cfg;
4182     },
4183     
4184     initEvents: function()
4185     {
4186         if (this.parent().type == 'treeview') {
4187             this.el.select('a').on('click', this.onClick, this);
4188         }
4189         
4190         if (this.menu) {
4191             this.menu.parentType = this.xtype;
4192             this.menu.triggerEl = this.el;
4193             this.menu = this.addxtype(Roo.apply({}, this.menu));
4194         }
4195         
4196     },
4197     onClick : function(e)
4198     {
4199         //Roo.log('item on click ');
4200         
4201         if(this.href === false || this.preventDefault){
4202             e.preventDefault();
4203         }
4204         //this.parent().hideMenuItems();
4205         
4206         this.fireEvent('click', this, e);
4207     },
4208     getEl : function()
4209     {
4210         return this.el;
4211     } 
4212 });
4213
4214  
4215
4216  
4217
4218   
4219 /**
4220  * @class Roo.bootstrap.menu.Separator
4221  * @extends Roo.bootstrap.Component
4222  * @licence LGPL
4223  * @parent Roo.bootstrap.menu.Menu
4224  * Bootstrap Separator class
4225  * 
4226  * @constructor
4227  * Create a new Separator
4228  * @param {Object} config The config object
4229  */
4230
4231
4232 Roo.bootstrap.menu.Separator = function(config){
4233     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4234 };
4235
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4237     
4238     getAutoCreate : function(){
4239         var cfg = {
4240             tag : 'li',
4241             cls: 'dropdown-divider divider'
4242         };
4243         
4244         return cfg;
4245     }
4246    
4247 });
4248
4249  
4250
4251  
4252 /*
4253 * Licence: LGPL
4254 */
4255
4256 /**
4257  * @class Roo.bootstrap.Modal
4258  * @extends Roo.bootstrap.Component
4259  * @parent none builder
4260  * @children Roo.bootstrap.Component
4261  * Bootstrap Modal class
4262  * @cfg {String} title Title of dialog
4263  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4265  * @cfg {Boolean} specificTitle default false
4266  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4267  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268  * @cfg {Boolean} animate default true
4269  * @cfg {Boolean} allow_close default true
4270  * @cfg {Boolean} fitwindow default false
4271  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274  * @cfg {String} size (sm|lg|xl) default empty
4275  * @cfg {Number} max_width set the max width of modal
4276  * @cfg {Boolean} editableTitle can the title be edited
4277
4278  *
4279  *
4280  * @constructor
4281  * Create a new Modal Dialog
4282  * @param {Object} config The config object
4283  */
4284
4285 Roo.bootstrap.Modal = function(config){
4286     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4287     this.addEvents({
4288         // raw events
4289         /**
4290          * @event btnclick
4291          * The raw btnclick event for the button
4292          * @param {Roo.EventObject} e
4293          */
4294         "btnclick" : true,
4295         /**
4296          * @event resize
4297          * Fire when dialog resize
4298          * @param {Roo.bootstrap.Modal} this
4299          * @param {Roo.EventObject} e
4300          */
4301         "resize" : true,
4302         /**
4303          * @event titlechanged
4304          * Fire when the editable title has been changed
4305          * @param {Roo.bootstrap.Modal} this
4306          * @param {Roo.EventObject} value
4307          */
4308         "titlechanged" : true 
4309         
4310     });
4311     this.buttons = this.buttons || [];
4312
4313     if (this.tmpl) {
4314         this.tmpl = Roo.factory(this.tmpl);
4315     }
4316
4317 };
4318
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4320
4321     title : 'test dialog',
4322
4323     buttons : false,
4324
4325     // set on load...
4326
4327     html: false,
4328
4329     tmp: false,
4330
4331     specificTitle: false,
4332
4333     buttonPosition: 'right',
4334
4335     allow_close : true,
4336
4337     animate : true,
4338
4339     fitwindow: false,
4340     
4341      // private
4342     dialogEl: false,
4343     bodyEl:  false,
4344     footerEl:  false,
4345     titleEl:  false,
4346     closeEl:  false,
4347
4348     size: '',
4349     
4350     max_width: 0,
4351     
4352     max_height: 0,
4353     
4354     fit_content: false,
4355     editableTitle  : false,
4356
4357     onRender : function(ct, position)
4358     {
4359         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4360
4361         if(!this.el){
4362             var cfg = Roo.apply({},  this.getAutoCreate());
4363             cfg.id = Roo.id();
4364             //if(!cfg.name){
4365             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4366             //}
4367             //if (!cfg.name.length) {
4368             //    delete cfg.name;
4369            // }
4370             if (this.cls) {
4371                 cfg.cls += ' ' + this.cls;
4372             }
4373             if (this.style) {
4374                 cfg.style = this.style;
4375             }
4376             this.el = Roo.get(document.body).createChild(cfg, position);
4377         }
4378         //var type = this.el.dom.type;
4379
4380
4381         if(this.tabIndex !== undefined){
4382             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4383         }
4384
4385         this.dialogEl = this.el.select('.modal-dialog',true).first();
4386         this.bodyEl = this.el.select('.modal-body',true).first();
4387         this.closeEl = this.el.select('.modal-header .close', true).first();
4388         this.headerEl = this.el.select('.modal-header',true).first();
4389         this.titleEl = this.el.select('.modal-title',true).first();
4390         this.footerEl = this.el.select('.modal-footer',true).first();
4391
4392         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4393         
4394         //this.el.addClass("x-dlg-modal");
4395
4396         if (this.buttons.length) {
4397             Roo.each(this.buttons, function(bb) {
4398                 var b = Roo.apply({}, bb);
4399                 b.xns = b.xns || Roo.bootstrap;
4400                 b.xtype = b.xtype || 'Button';
4401                 if (typeof(b.listeners) == 'undefined') {
4402                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4403                 }
4404
4405                 var btn = Roo.factory(b);
4406
4407                 btn.render(this.getButtonContainer());
4408
4409             },this);
4410         }
4411         // render the children.
4412         var nitems = [];
4413
4414         if(typeof(this.items) != 'undefined'){
4415             var items = this.items;
4416             delete this.items;
4417
4418             for(var i =0;i < items.length;i++) {
4419                 // we force children not to montor widnow resize  - as we do that for them.
4420                 items[i].monitorWindowResize = false;
4421                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628         // any layout/border etc.. resize..
4629         (function () {
4630             this.items.forEach( function(e) {
4631                 e.layout ? e.layout() : false;
4632
4633             });
4634         }).defer(100,this);
4635         
4636     },
4637
4638     show : function() {
4639
4640         if (!this.rendered) {
4641             this.render();
4642         }
4643         this.toggleHeaderInput(false);
4644         //this.el.setStyle('display', 'block');
4645         this.el.removeClass('hideing');
4646         this.el.dom.style.display='block';
4647         
4648         Roo.get(document.body).addClass('modal-open');
4649  
4650         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4651             
4652             (function(){
4653                 this.el.addClass('show');
4654                 this.el.addClass('in');
4655             }).defer(50, this);
4656         }else{
4657             this.el.addClass('show');
4658             this.el.addClass('in');
4659         }
4660
4661         // not sure how we can show data in here..
4662         //if (this.tmpl) {
4663         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4664         //}
4665
4666         Roo.get(document.body).addClass("x-body-masked");
4667         
4668         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4669         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4670         this.maskEl.dom.style.display = 'block';
4671         this.maskEl.addClass('show');
4672         
4673         
4674         this.resize();
4675         
4676         this.fireEvent('show', this);
4677
4678         // set zindex here - otherwise it appears to be ignored...
4679         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4680         
4681         
4682         // this is for children that are... layout.Border 
4683         (function () {
4684             this.items.forEach( function(e) {
4685                 e.layout ? e.layout() : false;
4686
4687             });
4688         }).defer(100,this);
4689
4690     },
4691     hide : function()
4692     {
4693         if(this.fireEvent("beforehide", this) !== false){
4694             
4695             this.maskEl.removeClass('show');
4696             
4697             this.maskEl.dom.style.display = '';
4698             Roo.get(document.body).removeClass("x-body-masked");
4699             this.el.removeClass('in');
4700             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4701
4702             if(this.animate){ // why
4703                 this.el.addClass('hideing');
4704                 this.el.removeClass('show');
4705                 (function(){
4706                     if (!this.el.hasClass('hideing')) {
4707                         return; // it's been shown again...
4708                     }
4709                     
4710                     this.el.dom.style.display='';
4711
4712                     Roo.get(document.body).removeClass('modal-open');
4713                     this.el.removeClass('hideing');
4714                 }).defer(150,this);
4715                 
4716             }else{
4717                 this.el.removeClass('show');
4718                 this.el.dom.style.display='';
4719                 Roo.get(document.body).removeClass('modal-open');
4720
4721             }
4722             this.fireEvent('hide', this);
4723         }
4724     },
4725     isVisible : function()
4726     {
4727         
4728         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4729         
4730     },
4731
4732     addButton : function(str, cb)
4733     {
4734
4735
4736         var b = Roo.apply({}, { html : str } );
4737         b.xns = b.xns || Roo.bootstrap;
4738         b.xtype = b.xtype || 'Button';
4739         if (typeof(b.listeners) == 'undefined') {
4740             b.listeners = { click : cb.createDelegate(this)  };
4741         }
4742
4743         var btn = Roo.factory(b);
4744
4745         btn.render(this.getButtonContainer());
4746
4747         return btn;
4748
4749     },
4750
4751     setDefaultButton : function(btn)
4752     {
4753         //this.el.select('.modal-footer').()
4754     },
4755
4756     resizeTo: function(w,h)
4757     {
4758         this.dialogEl.setWidth(w);
4759         
4760         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4761
4762         this.bodyEl.setHeight(h - diff);
4763         
4764         this.fireEvent('resize', this);
4765     },
4766     
4767     setContentSize  : function(w, h)
4768     {
4769
4770     },
4771     onButtonClick: function(btn,e)
4772     {
4773         //Roo.log([a,b,c]);
4774         this.fireEvent('btnclick', btn.name, e);
4775     },
4776      /**
4777      * Set the title of the Dialog
4778      * @param {String} str new Title
4779      */
4780     setTitle: function(str) {
4781         this.titleEl.dom.innerHTML = str;
4782         this.title = str;
4783     },
4784     /**
4785      * Set the body of the Dialog
4786      * @param {String} str new Title
4787      */
4788     setBody: function(str) {
4789         this.bodyEl.dom.innerHTML = str;
4790     },
4791     /**
4792      * Set the body of the Dialog using the template
4793      * @param {Obj} data - apply this data to the template and replace the body contents.
4794      */
4795     applyBody: function(obj)
4796     {
4797         if (!this.tmpl) {
4798             Roo.log("Error - using apply Body without a template");
4799             //code
4800         }
4801         this.tmpl.overwrite(this.bodyEl, obj);
4802     },
4803     
4804     getChildHeight : function(child_nodes)
4805     {
4806         if(
4807             !child_nodes ||
4808             child_nodes.length == 0
4809         ) {
4810             return 0;
4811         }
4812         
4813         var child_height = 0;
4814         
4815         for(var i = 0; i < child_nodes.length; i++) {
4816             
4817             /*
4818             * for modal with tabs...
4819             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4820                 
4821                 var layout_childs = child_nodes[i].childNodes;
4822                 
4823                 for(var j = 0; j < layout_childs.length; j++) {
4824                     
4825                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4826                         
4827                         var layout_body_childs = layout_childs[j].childNodes;
4828                         
4829                         for(var k = 0; k < layout_body_childs.length; k++) {
4830                             
4831                             if(layout_body_childs[k].classList.contains('navbar')) {
4832                                 child_height += layout_body_childs[k].offsetHeight;
4833                                 continue;
4834                             }
4835                             
4836                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4837                                 
4838                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4839                                 
4840                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4841                                     
4842                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4843                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4844                                         continue;
4845                                     }
4846                                     
4847                                 }
4848                                 
4849                             }
4850                             
4851                         }
4852                     }
4853                 }
4854                 continue;
4855             }
4856             */
4857             
4858             child_height += child_nodes[i].offsetHeight;
4859             // Roo.log(child_nodes[i].offsetHeight);
4860         }
4861         
4862         return child_height;
4863     },
4864     toggleHeaderInput : function(is_edit)
4865     {
4866         if (!this.editableTitle) {
4867             return; // not editable.
4868         }
4869         if (is_edit && this.is_header_editing) {
4870             return; // already editing..
4871         }
4872         if (is_edit) {
4873     
4874             this.headerEditEl.dom.value = this.title;
4875             this.headerEditEl.removeClass('d-none');
4876             this.headerEditEl.dom.focus();
4877             this.titleEl.addClass('d-none');
4878             
4879             this.is_header_editing = true;
4880             return
4881         }
4882         // flip back to not editing.
4883         this.title = this.headerEditEl.dom.value;
4884         this.headerEditEl.addClass('d-none');
4885         this.titleEl.removeClass('d-none');
4886         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4887         this.is_header_editing = false;
4888         this.fireEvent('titlechanged', this, this.title);
4889     
4890             
4891         
4892     }
4893
4894 });
4895
4896
4897 Roo.apply(Roo.bootstrap.Modal,  {
4898     /**
4899          * Button config that displays a single OK button
4900          * @type Object
4901          */
4902         OK :  [{
4903             name : 'ok',
4904             weight : 'primary',
4905             html : 'OK'
4906         }],
4907         /**
4908          * Button config that displays Yes and No buttons
4909          * @type Object
4910          */
4911         YESNO : [
4912             {
4913                 name  : 'no',
4914                 html : 'No'
4915             },
4916             {
4917                 name  :'yes',
4918                 weight : 'primary',
4919                 html : 'Yes'
4920             }
4921         ],
4922
4923         /**
4924          * Button config that displays OK and Cancel buttons
4925          * @type Object
4926          */
4927         OKCANCEL : [
4928             {
4929                name : 'cancel',
4930                 html : 'Cancel'
4931             },
4932             {
4933                 name : 'ok',
4934                 weight : 'primary',
4935                 html : 'OK'
4936             }
4937         ],
4938         /**
4939          * Button config that displays Yes, No and Cancel buttons
4940          * @type Object
4941          */
4942         YESNOCANCEL : [
4943             {
4944                 name : 'yes',
4945                 weight : 'primary',
4946                 html : 'Yes'
4947             },
4948             {
4949                 name : 'no',
4950                 html : 'No'
4951             },
4952             {
4953                 name : 'cancel',
4954                 html : 'Cancel'
4955             }
4956         ],
4957         
4958         zIndex : 10001
4959 });
4960
4961 /*
4962  * - LGPL
4963  *
4964  * messagebox - can be used as a replace
4965  * 
4966  */
4967 /**
4968  * @class Roo.MessageBox
4969  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4970  * Example usage:
4971  *<pre><code>
4972 // Basic alert:
4973 Roo.Msg.alert('Status', 'Changes saved successfully.');
4974
4975 // Prompt for user data:
4976 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4977     if (btn == 'ok'){
4978         // process text value...
4979     }
4980 });
4981
4982 // Show a dialog using config options:
4983 Roo.Msg.show({
4984    title:'Save Changes?',
4985    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4986    buttons: Roo.Msg.YESNOCANCEL,
4987    fn: processResult,
4988    animEl: 'elId'
4989 });
4990 </code></pre>
4991  * @static
4992  */
4993 Roo.bootstrap.MessageBox = function(){
4994     var dlg, opt, mask, waitTimer;
4995     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4996     var buttons, activeTextEl, bwidth;
4997
4998     
4999     // private
5000     var handleButton = function(button){
5001         dlg.hide();
5002         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5003     };
5004
5005     // private
5006     var handleHide = function(){
5007         if(opt && opt.cls){
5008             dlg.el.removeClass(opt.cls);
5009         }
5010         //if(waitTimer){
5011         //    Roo.TaskMgr.stop(waitTimer);
5012         //    waitTimer = null;
5013         //}
5014     };
5015
5016     // private
5017     var updateButtons = function(b){
5018         var width = 0;
5019         if(!b){
5020             buttons["ok"].hide();
5021             buttons["cancel"].hide();
5022             buttons["yes"].hide();
5023             buttons["no"].hide();
5024             dlg.footerEl.hide();
5025             
5026             return width;
5027         }
5028         dlg.footerEl.show();
5029         for(var k in buttons){
5030             if(typeof buttons[k] != "function"){
5031                 if(b[k]){
5032                     buttons[k].show();
5033                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5034                     width += buttons[k].el.getWidth()+15;
5035                 }else{
5036                     buttons[k].hide();
5037                 }
5038             }
5039         }
5040         return width;
5041     };
5042
5043     // private
5044     var handleEsc = function(d, k, e){
5045         if(opt && opt.closable !== false){
5046             dlg.hide();
5047         }
5048         if(e){
5049             e.stopEvent();
5050         }
5051     };
5052
5053     return {
5054         /**
5055          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5056          * @return {Roo.BasicDialog} The BasicDialog element
5057          */
5058         getDialog : function(){
5059            if(!dlg){
5060                 dlg = new Roo.bootstrap.Modal( {
5061                     //draggable: true,
5062                     //resizable:false,
5063                     //constraintoviewport:false,
5064                     //fixedcenter:true,
5065                     //collapsible : false,
5066                     //shim:true,
5067                     //modal: true,
5068                 //    width: 'auto',
5069                   //  height:100,
5070                     //buttonAlign:"center",
5071                     closeClick : function(){
5072                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5073                             handleButton("no");
5074                         }else{
5075                             handleButton("cancel");
5076                         }
5077                     }
5078                 });
5079                 dlg.render();
5080                 dlg.on("hide", handleHide);
5081                 mask = dlg.mask;
5082                 //dlg.addKeyListener(27, handleEsc);
5083                 buttons = {};
5084                 this.buttons = buttons;
5085                 var bt = this.buttonText;
5086                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5087                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5088                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5089                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5090                 //Roo.log(buttons);
5091                 bodyEl = dlg.bodyEl.createChild({
5092
5093                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5094                         '<textarea class="roo-mb-textarea"></textarea>' +
5095                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5096                 });
5097                 msgEl = bodyEl.dom.firstChild;
5098                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5099                 textboxEl.enableDisplayMode();
5100                 textboxEl.addKeyListener([10,13], function(){
5101                     if(dlg.isVisible() && opt && opt.buttons){
5102                         if(opt.buttons.ok){
5103                             handleButton("ok");
5104                         }else if(opt.buttons.yes){
5105                             handleButton("yes");
5106                         }
5107                     }
5108                 });
5109                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5110                 textareaEl.enableDisplayMode();
5111                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5112                 progressEl.enableDisplayMode();
5113                 
5114                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5115                 var pf = progressEl.dom.firstChild;
5116                 if (pf) {
5117                     pp = Roo.get(pf.firstChild);
5118                     pp.setHeight(pf.offsetHeight);
5119                 }
5120                 
5121             }
5122             return dlg;
5123         },
5124
5125         /**
5126          * Updates the message box body text
5127          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5128          * the XHTML-compliant non-breaking space character '&amp;#160;')
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateText : function(text)
5132         {
5133             if(!dlg.isVisible() && !opt.width){
5134                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5135                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5136             }
5137             msgEl.innerHTML = text || '&#160;';
5138       
5139             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5140             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5141             var w = Math.max(
5142                     Math.min(opt.width || cw , this.maxWidth), 
5143                     Math.max(opt.minWidth || this.minWidth, bwidth)
5144             );
5145             if(opt.prompt){
5146                 activeTextEl.setWidth(w);
5147             }
5148             if(dlg.isVisible()){
5149                 dlg.fixedcenter = false;
5150             }
5151             // to big, make it scroll. = But as usual stupid IE does not support
5152             // !important..
5153             
5154             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5155                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5156                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5157             } else {
5158                 bodyEl.dom.style.height = '';
5159                 bodyEl.dom.style.overflowY = '';
5160             }
5161             if (cw > w) {
5162                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5163             } else {
5164                 bodyEl.dom.style.overflowX = '';
5165             }
5166             
5167             dlg.setContentSize(w, bodyEl.getHeight());
5168             if(dlg.isVisible()){
5169                 dlg.fixedcenter = true;
5170             }
5171             return this;
5172         },
5173
5174         /**
5175          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5176          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5177          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5178          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5179          * @return {Roo.MessageBox} This message box
5180          */
5181         updateProgress : function(value, text){
5182             if(text){
5183                 this.updateText(text);
5184             }
5185             
5186             if (pp) { // weird bug on my firefox - for some reason this is not defined
5187                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5188                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5189             }
5190             return this;
5191         },        
5192
5193         /**
5194          * Returns true if the message box is currently displayed
5195          * @return {Boolean} True if the message box is visible, else false
5196          */
5197         isVisible : function(){
5198             return dlg && dlg.isVisible();  
5199         },
5200
5201         /**
5202          * Hides the message box if it is displayed
5203          */
5204         hide : function(){
5205             if(this.isVisible()){
5206                 dlg.hide();
5207             }  
5208         },
5209
5210         /**
5211          * Displays a new message box, or reinitializes an existing message box, based on the config options
5212          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5213          * The following config object properties are supported:
5214          * <pre>
5215 Property    Type             Description
5216 ----------  ---------------  ------------------------------------------------------------------------------------
5217 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5218                                    closes (defaults to undefined)
5219 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5220                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5221 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5222                                    progress and wait dialogs will ignore this property and always hide the
5223                                    close button as they can only be closed programmatically.
5224 cls               String           A custom CSS class to apply to the message box element
5225 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5226                                    displayed (defaults to 75)
5227 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5228                                    function will be btn (the name of the button that was clicked, if applicable,
5229                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5230                                    Progress and wait dialogs will ignore this option since they do not respond to
5231                                    user actions and can only be closed programmatically, so any required function
5232                                    should be called by the same code after it closes the dialog.
5233 icon              String           A CSS class that provides a background image to be used as an icon for
5234                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5235 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5236 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5237 modal             Boolean          False to allow user interaction with the page while the message box is
5238                                    displayed (defaults to true)
5239 msg               String           A string that will replace the existing message box body text (defaults
5240                                    to the XHTML-compliant non-breaking space character '&#160;')
5241 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5242 progress          Boolean          True to display a progress bar (defaults to false)
5243 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5244 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5245 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5246 title             String           The title text
5247 value             String           The string value to set into the active textbox element if displayed
5248 wait              Boolean          True to display a progress bar (defaults to false)
5249 width             Number           The width of the dialog in pixels
5250 </pre>
5251          *
5252          * Example usage:
5253          * <pre><code>
5254 Roo.Msg.show({
5255    title: 'Address',
5256    msg: 'Please enter your address:',
5257    width: 300,
5258    buttons: Roo.MessageBox.OKCANCEL,
5259    multiline: true,
5260    fn: saveAddress,
5261    animEl: 'addAddressBtn'
5262 });
5263 </code></pre>
5264          * @param {Object} config Configuration options
5265          * @return {Roo.MessageBox} This message box
5266          */
5267         show : function(options)
5268         {
5269             
5270             // this causes nightmares if you show one dialog after another
5271             // especially on callbacks..
5272              
5273             if(this.isVisible()){
5274                 
5275                 this.hide();
5276                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5277                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5278                 Roo.log("New Dialog Message:" +  options.msg )
5279                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5280                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5281                 
5282             }
5283             var d = this.getDialog();
5284             opt = options;
5285             d.setTitle(opt.title || "&#160;");
5286             d.closeEl.setDisplayed(opt.closable !== false);
5287             activeTextEl = textboxEl;
5288             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5289             if(opt.prompt){
5290                 if(opt.multiline){
5291                     textboxEl.hide();
5292                     textareaEl.show();
5293                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5294                         opt.multiline : this.defaultTextHeight);
5295                     activeTextEl = textareaEl;
5296                 }else{
5297                     textboxEl.show();
5298                     textareaEl.hide();
5299                 }
5300             }else{
5301                 textboxEl.hide();
5302                 textareaEl.hide();
5303             }
5304             progressEl.setDisplayed(opt.progress === true);
5305             if (opt.progress) {
5306                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5307             }
5308             this.updateProgress(0);
5309             activeTextEl.dom.value = opt.value || "";
5310             if(opt.prompt){
5311                 dlg.setDefaultButton(activeTextEl);
5312             }else{
5313                 var bs = opt.buttons;
5314                 var db = null;
5315                 if(bs && bs.ok){
5316                     db = buttons["ok"];
5317                 }else if(bs && bs.yes){
5318                     db = buttons["yes"];
5319                 }
5320                 dlg.setDefaultButton(db);
5321             }
5322             bwidth = updateButtons(opt.buttons);
5323             this.updateText(opt.msg);
5324             if(opt.cls){
5325                 d.el.addClass(opt.cls);
5326             }
5327             d.proxyDrag = opt.proxyDrag === true;
5328             d.modal = opt.modal !== false;
5329             d.mask = opt.modal !== false ? mask : false;
5330             if(!d.isVisible()){
5331                 // force it to the end of the z-index stack so it gets a cursor in FF
5332                 document.body.appendChild(dlg.el.dom);
5333                 d.animateTarget = null;
5334                 d.show(options.animEl);
5335             }
5336             return this;
5337         },
5338
5339         /**
5340          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5341          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5342          * and closing the message box when the process is complete.
5343          * @param {String} title The title bar text
5344          * @param {String} msg The message box body text
5345          * @return {Roo.MessageBox} This message box
5346          */
5347         progress : function(title, msg){
5348             this.show({
5349                 title : title,
5350                 msg : msg,
5351                 buttons: false,
5352                 progress:true,
5353                 closable:false,
5354                 minWidth: this.minProgressWidth,
5355                 modal : true
5356             });
5357             return this;
5358         },
5359
5360         /**
5361          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5362          * If a callback function is passed it will be called after the user clicks the button, and the
5363          * id of the button that was clicked will be passed as the only parameter to the callback
5364          * (could also be the top-right close button).
5365          * @param {String} title The title bar text
5366          * @param {String} msg The message box body text
5367          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5368          * @param {Object} scope (optional) The scope of the callback function
5369          * @return {Roo.MessageBox} This message box
5370          */
5371         alert : function(title, msg, fn, scope)
5372         {
5373             this.show({
5374                 title : title,
5375                 msg : msg,
5376                 buttons: this.OK,
5377                 fn: fn,
5378                 closable : false,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5387          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5388          * You are responsible for closing the message box when the process is complete.
5389          * @param {String} msg The message box body text
5390          * @param {String} title (optional) The title bar text
5391          * @return {Roo.MessageBox} This message box
5392          */
5393         wait : function(msg, title){
5394             this.show({
5395                 title : title,
5396                 msg : msg,
5397                 buttons: false,
5398                 closable:false,
5399                 progress:true,
5400                 modal:true,
5401                 width:300,
5402                 wait:true
5403             });
5404             waitTimer = Roo.TaskMgr.start({
5405                 run: function(i){
5406                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5407                 },
5408                 interval: 1000
5409             });
5410             return this;
5411         },
5412
5413         /**
5414          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5415          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5416          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5417          * @param {String} title The title bar text
5418          * @param {String} msg The message box body text
5419          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5420          * @param {Object} scope (optional) The scope of the callback function
5421          * @return {Roo.MessageBox} This message box
5422          */
5423         confirm : function(title, msg, fn, scope){
5424             this.show({
5425                 title : title,
5426                 msg : msg,
5427                 buttons: this.YESNO,
5428                 fn: fn,
5429                 scope : scope,
5430                 modal : true
5431             });
5432             return this;
5433         },
5434
5435         /**
5436          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5437          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5438          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5439          * (could also be the top-right close button) and the text that was entered will be passed as the two
5440          * parameters to the callback.
5441          * @param {String} title The title bar text
5442          * @param {String} msg The message box body text
5443          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5444          * @param {Object} scope (optional) The scope of the callback function
5445          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5446          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5447          * @return {Roo.MessageBox} This message box
5448          */
5449         prompt : function(title, msg, fn, scope, multiline){
5450             this.show({
5451                 title : title,
5452                 msg : msg,
5453                 buttons: this.OKCANCEL,
5454                 fn: fn,
5455                 minWidth:250,
5456                 scope : scope,
5457                 prompt:true,
5458                 multiline: multiline,
5459                 modal : true
5460             });
5461             return this;
5462         },
5463
5464         /**
5465          * Button config that displays a single OK button
5466          * @type Object
5467          */
5468         OK : {ok:true},
5469         /**
5470          * Button config that displays Yes and No buttons
5471          * @type Object
5472          */
5473         YESNO : {yes:true, no:true},
5474         /**
5475          * Button config that displays OK and Cancel buttons
5476          * @type Object
5477          */
5478         OKCANCEL : {ok:true, cancel:true},
5479         /**
5480          * Button config that displays Yes, No and Cancel buttons
5481          * @type Object
5482          */
5483         YESNOCANCEL : {yes:true, no:true, cancel:true},
5484
5485         /**
5486          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5487          * @type Number
5488          */
5489         defaultTextHeight : 75,
5490         /**
5491          * The maximum width in pixels of the message box (defaults to 600)
5492          * @type Number
5493          */
5494         maxWidth : 600,
5495         /**
5496          * The minimum width in pixels of the message box (defaults to 100)
5497          * @type Number
5498          */
5499         minWidth : 100,
5500         /**
5501          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5502          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5503          * @type Number
5504          */
5505         minProgressWidth : 250,
5506         /**
5507          * An object containing the default button text strings that can be overriden for localized language support.
5508          * Supported properties are: ok, cancel, yes and no.
5509          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5510          * @type Object
5511          */
5512         buttonText : {
5513             ok : "OK",
5514             cancel : "Cancel",
5515             yes : "Yes",
5516             no : "No"
5517         }
5518     };
5519 }();
5520
5521 /**
5522  * Shorthand for {@link Roo.MessageBox}
5523  */
5524 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5525 Roo.Msg = Roo.Msg || Roo.MessageBox;
5526 /*
5527  * - LGPL
5528  *
5529  * navbar
5530  * 
5531  */
5532
5533 /**
5534  * @class Roo.bootstrap.nav.Bar
5535  * @extends Roo.bootstrap.Component
5536  * @abstract
5537  * Bootstrap Navbar class
5538
5539  * @constructor
5540  * Create a new Navbar
5541  * @param {Object} config The config object
5542  */
5543
5544
5545 Roo.bootstrap.nav.Bar = function(config){
5546     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5547     this.addEvents({
5548         // raw events
5549         /**
5550          * @event beforetoggle
5551          * Fire before toggle the menu
5552          * @param {Roo.EventObject} e
5553          */
5554         "beforetoggle" : true
5555     });
5556 };
5557
5558 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5559     
5560     
5561    
5562     // private
5563     navItems : false,
5564     loadMask : false,
5565     
5566     
5567     getAutoCreate : function(){
5568         
5569         
5570         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5571         
5572     },
5573     
5574     initEvents :function ()
5575     {
5576         //Roo.log(this.el.select('.navbar-toggle',true));
5577         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5578         
5579         var mark = {
5580             tag: "div",
5581             cls:"x-dlg-mask"
5582         };
5583         
5584         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5585         
5586         var size = this.el.getSize();
5587         this.maskEl.setSize(size.width, size.height);
5588         this.maskEl.enableDisplayMode("block");
5589         this.maskEl.hide();
5590         
5591         if(this.loadMask){
5592             this.maskEl.show();
5593         }
5594     },
5595     
5596     
5597     getChildContainer : function()
5598     {
5599         if (this.el && this.el.select('.collapse').getCount()) {
5600             return this.el.select('.collapse',true).first();
5601         }
5602         
5603         return this.el;
5604     },
5605     
5606     mask : function()
5607     {
5608         this.maskEl.show();
5609     },
5610     
5611     unmask : function()
5612     {
5613         this.maskEl.hide();
5614     },
5615     onToggle : function()
5616     {
5617         
5618         if(this.fireEvent('beforetoggle', this) === false){
5619             return;
5620         }
5621         var ce = this.el.select('.navbar-collapse',true).first();
5622       
5623         if (!ce.hasClass('show')) {
5624            this.expand();
5625         } else {
5626             this.collapse();
5627         }
5628         
5629         
5630     
5631     },
5632     /**
5633      * Expand the navbar pulldown 
5634      */
5635     expand : function ()
5636     {
5637        
5638         var ce = this.el.select('.navbar-collapse',true).first();
5639         if (ce.hasClass('collapsing')) {
5640             return;
5641         }
5642         ce.dom.style.height = '';
5643                // show it...
5644         ce.addClass('in'); // old...
5645         ce.removeClass('collapse');
5646         ce.addClass('show');
5647         var h = ce.getHeight();
5648         Roo.log(h);
5649         ce.removeClass('show');
5650         // at this point we should be able to see it..
5651         ce.addClass('collapsing');
5652         
5653         ce.setHeight(0); // resize it ...
5654         ce.on('transitionend', function() {
5655             //Roo.log('done transition');
5656             ce.removeClass('collapsing');
5657             ce.addClass('show');
5658             ce.removeClass('collapse');
5659
5660             ce.dom.style.height = '';
5661         }, this, { single: true} );
5662         ce.setHeight(h);
5663         ce.dom.scrollTop = 0;
5664     },
5665     /**
5666      * Collapse the navbar pulldown 
5667      */
5668     collapse : function()
5669     {
5670          var ce = this.el.select('.navbar-collapse',true).first();
5671        
5672         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5673             // it's collapsed or collapsing..
5674             return;
5675         }
5676         ce.removeClass('in'); // old...
5677         ce.setHeight(ce.getHeight());
5678         ce.removeClass('show');
5679         ce.addClass('collapsing');
5680         
5681         ce.on('transitionend', function() {
5682             ce.dom.style.height = '';
5683             ce.removeClass('collapsing');
5684             ce.addClass('collapse');
5685         }, this, { single: true} );
5686         ce.setHeight(0);
5687     }
5688     
5689     
5690     
5691 });
5692
5693
5694
5695  
5696
5697  /*
5698  * - LGPL
5699  *
5700  * navbar
5701  * 
5702  */
5703
5704 /**
5705  * @class Roo.bootstrap.nav.Simplebar
5706  * @extends Roo.bootstrap.nav.Bar
5707  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5708  * Bootstrap Sidebar class
5709  *
5710  * @cfg {Boolean} inverse is inverted color
5711  * 
5712  * @cfg {String} type (nav | pills | tabs)
5713  * @cfg {Boolean} arrangement stacked | justified
5714  * @cfg {String} align (left | right) alignment
5715  * 
5716  * @cfg {Boolean} main (true|false) main nav bar? default false
5717  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5718  * 
5719  * @cfg {String} tag (header|footer|nav|div) default is nav 
5720
5721  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5722  * 
5723  * 
5724  * @constructor
5725  * Create a new Sidebar
5726  * @param {Object} config The config object
5727  */
5728
5729
5730 Roo.bootstrap.nav.Simplebar = function(config){
5731     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5732 };
5733
5734 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5735     
5736     inverse: false,
5737     
5738     type: false,
5739     arrangement: '',
5740     align : false,
5741     
5742     weight : 'light',
5743     
5744     main : false,
5745     
5746     
5747     tag : false,
5748     
5749     
5750     getAutoCreate : function(){
5751         
5752         
5753         var cfg = {
5754             tag : this.tag || 'div',
5755             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5756         };
5757         if (['light','white'].indexOf(this.weight) > -1) {
5758             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5759         }
5760         cfg.cls += ' bg-' + this.weight;
5761         
5762         if (this.inverse) {
5763             cfg.cls += ' navbar-inverse';
5764             
5765         }
5766         
5767         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5768         
5769         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5770             return cfg;
5771         }
5772         
5773         
5774     
5775         
5776         cfg.cn = [
5777             {
5778                 cls: 'nav nav-' + this.xtype,
5779                 tag : 'ul'
5780             }
5781         ];
5782         
5783          
5784         this.type = this.type || 'nav';
5785         if (['tabs','pills'].indexOf(this.type) != -1) {
5786             cfg.cn[0].cls += ' nav-' + this.type
5787         
5788         
5789         } else {
5790             if (this.type!=='nav') {
5791                 Roo.log('nav type must be nav/tabs/pills')
5792             }
5793             cfg.cn[0].cls += ' navbar-nav'
5794         }
5795         
5796         
5797         
5798         
5799         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5800             cfg.cn[0].cls += ' nav-' + this.arrangement;
5801         }
5802         
5803         
5804         if (this.align === 'right') {
5805             cfg.cn[0].cls += ' navbar-right';
5806         }
5807         
5808         
5809         
5810         
5811         return cfg;
5812     
5813         
5814     }
5815     
5816     
5817     
5818 });
5819
5820
5821
5822  
5823
5824  
5825        /*
5826  * - LGPL
5827  *
5828  * navbar
5829  * navbar-fixed-top
5830  * navbar-expand-md  fixed-top 
5831  */
5832
5833 /**
5834  * @class Roo.bootstrap.nav.Headerbar
5835  * @extends Roo.bootstrap.nav.Simplebar
5836  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5837  * Bootstrap Sidebar class
5838  *
5839  * @cfg {String} brand what is brand
5840  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5841  * @cfg {String} brand_href href of the brand
5842  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5843  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5844  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5845  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5846  * 
5847  * @constructor
5848  * Create a new Sidebar
5849  * @param {Object} config The config object
5850  */
5851
5852
5853 Roo.bootstrap.nav.Headerbar = function(config){
5854     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5855       
5856 };
5857
5858 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5859     
5860     position: '',
5861     brand: '',
5862     brand_href: false,
5863     srButton : true,
5864     autohide : false,
5865     desktopCenter : false,
5866    
5867     
5868     getAutoCreate : function(){
5869         
5870         var   cfg = {
5871             tag: this.nav || 'nav',
5872             cls: 'navbar navbar-expand-md',
5873             role: 'navigation',
5874             cn: []
5875         };
5876         
5877         var cn = cfg.cn;
5878         if (this.desktopCenter) {
5879             cn.push({cls : 'container', cn : []});
5880             cn = cn[0].cn;
5881         }
5882         
5883         if(this.srButton){
5884             var btn = {
5885                 tag: 'button',
5886                 type: 'button',
5887                 cls: 'navbar-toggle navbar-toggler',
5888                 'data-toggle': 'collapse',
5889                 cn: [
5890                     {
5891                         tag: 'span',
5892                         cls: 'sr-only',
5893                         html: 'Toggle navigation'
5894                     },
5895                     {
5896                         tag: 'span',
5897                         cls: 'icon-bar navbar-toggler-icon'
5898                     },
5899                     {
5900                         tag: 'span',
5901                         cls: 'icon-bar'
5902                     },
5903                     {
5904                         tag: 'span',
5905                         cls: 'icon-bar'
5906                     }
5907                 ]
5908             };
5909             
5910             cn.push( Roo.bootstrap.version == 4 ? btn : {
5911                 tag: 'div',
5912                 cls: 'navbar-header',
5913                 cn: [
5914                     btn
5915                 ]
5916             });
5917         }
5918         
5919         cn.push({
5920             tag: 'div',
5921             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5922             cn : []
5923         });
5924         
5925         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5926         
5927         if (['light','white'].indexOf(this.weight) > -1) {
5928             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5929         }
5930         cfg.cls += ' bg-' + this.weight;
5931         
5932         
5933         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5934             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5935             
5936             // tag can override this..
5937             
5938             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5939         }
5940         
5941         if (this.brand !== '') {
5942             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5943             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5944                 tag: 'a',
5945                 href: this.brand_href ? this.brand_href : '#',
5946                 cls: 'navbar-brand',
5947                 cn: [
5948                 this.brand
5949                 ]
5950             });
5951         }
5952         
5953         if(this.main){
5954             cfg.cls += ' main-nav';
5955         }
5956         
5957         
5958         return cfg;
5959
5960         
5961     },
5962     getHeaderChildContainer : function()
5963     {
5964         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5965             return this.el.select('.navbar-header',true).first();
5966         }
5967         
5968         return this.getChildContainer();
5969     },
5970     
5971     getChildContainer : function()
5972     {
5973          
5974         return this.el.select('.roo-navbar-collapse',true).first();
5975          
5976         
5977     },
5978     
5979     initEvents : function()
5980     {
5981         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5982         
5983         if (this.autohide) {
5984             
5985             var prevScroll = 0;
5986             var ft = this.el;
5987             
5988             Roo.get(document).on('scroll',function(e) {
5989                 var ns = Roo.get(document).getScroll().top;
5990                 var os = prevScroll;
5991                 prevScroll = ns;
5992                 
5993                 if(ns > os){
5994                     ft.removeClass('slideDown');
5995                     ft.addClass('slideUp');
5996                     return;
5997                 }
5998                 ft.removeClass('slideUp');
5999                 ft.addClass('slideDown');
6000                  
6001               
6002           },this);
6003         }
6004     }    
6005     
6006 });
6007
6008
6009
6010  
6011
6012  /*
6013  * - LGPL
6014  *
6015  * navbar
6016  * 
6017  */
6018
6019 /**
6020  * @class Roo.bootstrap.nav.Sidebar
6021  * @extends Roo.bootstrap.nav.Bar
6022  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6023  * Bootstrap Sidebar class
6024  * 
6025  * @constructor
6026  * Create a new Sidebar
6027  * @param {Object} config The config object
6028  */
6029
6030
6031 Roo.bootstrap.nav.Sidebar = function(config){
6032     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6033 };
6034
6035 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6036     
6037     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6038     
6039     getAutoCreate : function(){
6040         
6041         
6042         return  {
6043             tag: 'div',
6044             cls: 'sidebar sidebar-nav'
6045         };
6046     
6047         
6048     }
6049     
6050     
6051     
6052 });
6053
6054
6055
6056  
6057
6058  /*
6059  * - LGPL
6060  *
6061  * nav group
6062  * 
6063  */
6064
6065 /**
6066  * @class Roo.bootstrap.nav.Group
6067  * @extends Roo.bootstrap.Component
6068  * @children Roo.bootstrap.nav.Item
6069  * Bootstrap NavGroup class
6070  * @cfg {String} align (left|right)
6071  * @cfg {Boolean} inverse
6072  * @cfg {String} type (nav|pills|tab) default nav
6073  * @cfg {String} navId - reference Id for navbar.
6074  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6075  * 
6076  * @constructor
6077  * Create a new nav group
6078  * @param {Object} config The config object
6079  */
6080
6081 Roo.bootstrap.nav.Group = function(config){
6082     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6083     this.navItems = [];
6084    
6085     Roo.bootstrap.nav.Group.register(this);
6086      this.addEvents({
6087         /**
6088              * @event changed
6089              * Fires when the active item changes
6090              * @param {Roo.bootstrap.nav.Group} this
6091              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6092              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6093          */
6094         'changed': true
6095      });
6096     
6097 };
6098
6099 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6100     
6101     align: '',
6102     inverse: false,
6103     form: false,
6104     type: 'nav',
6105     navId : '',
6106     // private
6107     pilltype : true,
6108     
6109     navItems : false, 
6110     
6111     getAutoCreate : function()
6112     {
6113         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6114         
6115         cfg = {
6116             tag : 'ul',
6117             cls: 'nav' 
6118         };
6119         if (Roo.bootstrap.version == 4) {
6120             if (['tabs','pills'].indexOf(this.type) != -1) {
6121                 cfg.cls += ' nav-' + this.type; 
6122             } else {
6123                 // trying to remove so header bar can right align top?
6124                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6125                     // do not use on header bar... 
6126                     cfg.cls += ' navbar-nav';
6127                 }
6128             }
6129             
6130         } else {
6131             if (['tabs','pills'].indexOf(this.type) != -1) {
6132                 cfg.cls += ' nav-' + this.type
6133             } else {
6134                 if (this.type !== 'nav') {
6135                     Roo.log('nav type must be nav/tabs/pills')
6136                 }
6137                 cfg.cls += ' navbar-nav'
6138             }
6139         }
6140         
6141         if (this.parent() && this.parent().sidebar) {
6142             cfg = {
6143                 tag: 'ul',
6144                 cls: 'dashboard-menu sidebar-menu'
6145             };
6146             
6147             return cfg;
6148         }
6149         
6150         if (this.form === true) {
6151             cfg = {
6152                 tag: 'form',
6153                 cls: 'navbar-form form-inline'
6154             };
6155             //nav navbar-right ml-md-auto
6156             if (this.align === 'right') {
6157                 cfg.cls += ' navbar-right ml-md-auto';
6158             } else {
6159                 cfg.cls += ' navbar-left';
6160             }
6161         }
6162         
6163         if (this.align === 'right') {
6164             cfg.cls += ' navbar-right ml-md-auto';
6165         } else {
6166             cfg.cls += ' mr-auto';
6167         }
6168         
6169         if (this.inverse) {
6170             cfg.cls += ' navbar-inverse';
6171             
6172         }
6173         
6174         
6175         return cfg;
6176     },
6177     /**
6178     * sets the active Navigation item
6179     * @param {Roo.bootstrap.nav.Item} the new current navitem
6180     */
6181     setActiveItem : function(item)
6182     {
6183         var prev = false;
6184         Roo.each(this.navItems, function(v){
6185             if (v == item) {
6186                 return ;
6187             }
6188             if (v.isActive()) {
6189                 v.setActive(false, true);
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195
6196         item.setActive(true, true);
6197         this.fireEvent('changed', this, item, prev);
6198         
6199         
6200     },
6201     /**
6202     * gets the active Navigation item
6203     * @return {Roo.bootstrap.nav.Item} the current navitem
6204     */
6205     getActive : function()
6206     {
6207         
6208         var prev = false;
6209         Roo.each(this.navItems, function(v){
6210             
6211             if (v.isActive()) {
6212                 prev = v;
6213                 
6214             }
6215             
6216         });
6217         return prev;
6218     },
6219     
6220     indexOfNav : function()
6221     {
6222         
6223         var prev = false;
6224         Roo.each(this.navItems, function(v,i){
6225             
6226             if (v.isActive()) {
6227                 prev = i;
6228                 
6229             }
6230             
6231         });
6232         return prev;
6233     },
6234     /**
6235     * adds a Navigation item
6236     * @param {Roo.bootstrap.nav.Item} the navitem to add
6237     */
6238     addItem : function(cfg)
6239     {
6240         if (this.form && Roo.bootstrap.version == 4) {
6241             cfg.tag = 'div';
6242         }
6243         var cn = new Roo.bootstrap.nav.Item(cfg);
6244         this.register(cn);
6245         cn.parentId = this.id;
6246         cn.onRender(this.el, null);
6247         return cn;
6248     },
6249     /**
6250     * register a Navigation item
6251     * @param {Roo.bootstrap.nav.Item} the navitem to add
6252     */
6253     register : function(item)
6254     {
6255         this.navItems.push( item);
6256         item.navId = this.navId;
6257     
6258     },
6259     
6260     /**
6261     * clear all the Navigation item
6262     */
6263    
6264     clearAll : function()
6265     {
6266         this.navItems = [];
6267         this.el.dom.innerHTML = '';
6268     },
6269     
6270     getNavItem: function(tabId)
6271     {
6272         var ret = false;
6273         Roo.each(this.navItems, function(e) {
6274             if (e.tabId == tabId) {
6275                ret =  e;
6276                return false;
6277             }
6278             return true;
6279             
6280         });
6281         return ret;
6282     },
6283     
6284     setActiveNext : function()
6285     {
6286         var i = this.indexOfNav(this.getActive());
6287         if (i > this.navItems.length) {
6288             return;
6289         }
6290         this.setActiveItem(this.navItems[i+1]);
6291     },
6292     setActivePrev : function()
6293     {
6294         var i = this.indexOfNav(this.getActive());
6295         if (i  < 1) {
6296             return;
6297         }
6298         this.setActiveItem(this.navItems[i-1]);
6299     },
6300     clearWasActive : function(except) {
6301         Roo.each(this.navItems, function(e) {
6302             if (e.tabId != except.tabId && e.was_active) {
6303                e.was_active = false;
6304                return false;
6305             }
6306             return true;
6307             
6308         });
6309     },
6310     getWasActive : function ()
6311     {
6312         var r = false;
6313         Roo.each(this.navItems, function(e) {
6314             if (e.was_active) {
6315                r = e;
6316                return false;
6317             }
6318             return true;
6319             
6320         });
6321         return r;
6322     }
6323     
6324     
6325 });
6326
6327  
6328 Roo.apply(Roo.bootstrap.nav.Group, {
6329     
6330     groups: {},
6331      /**
6332     * register a Navigation Group
6333     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6334     */
6335     register : function(navgrp)
6336     {
6337         this.groups[navgrp.navId] = navgrp;
6338         
6339     },
6340     /**
6341     * fetch a Navigation Group based on the navigation ID
6342     * @param {string} the navgroup to add
6343     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6344     */
6345     get: function(navId) {
6346         if (typeof(this.groups[navId]) == 'undefined') {
6347             return false;
6348             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6349         }
6350         return this.groups[navId] ;
6351     }
6352     
6353     
6354     
6355 });
6356
6357  /**
6358  * @class Roo.bootstrap.nav.Item
6359  * @extends Roo.bootstrap.Component
6360  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6361  * @parent Roo.bootstrap.nav.Group
6362  * @licence LGPL
6363  * Bootstrap Navbar.NavItem class
6364  * 
6365  * @cfg {String} href  link to
6366  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6367  * @cfg {Boolean} button_outline show and outlined button
6368  * @cfg {String} html content of button
6369  * @cfg {String} badge text inside badge
6370  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6371  * @cfg {String} glyphicon DEPRICATED - use fa
6372  * @cfg {String} icon DEPRICATED - use fa
6373  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6374  * @cfg {Boolean} active Is item active
6375  * @cfg {Boolean} disabled Is item disabled
6376  * @cfg {String} linkcls  Link Class
6377  * @cfg {Boolean} preventDefault (true | false) default false
6378  * @cfg {String} tabId the tab that this item activates.
6379  * @cfg {String} tagtype (a|span) render as a href or span?
6380  * @cfg {Boolean} animateRef (true|false) link to element default false  
6381  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6382   
6383  * @constructor
6384  * Create a new Navbar Item
6385  * @param {Object} config The config object
6386  */
6387 Roo.bootstrap.nav.Item = function(config){
6388     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6389     this.addEvents({
6390         // raw events
6391         /**
6392          * @event click
6393          * The raw click event for the entire grid.
6394          * @param {Roo.EventObject} e
6395          */
6396         "click" : true,
6397          /**
6398             * @event changed
6399             * Fires when the active item active state changes
6400             * @param {Roo.bootstrap.nav.Item} this
6401             * @param {boolean} state the new state
6402              
6403          */
6404         'changed': true,
6405         /**
6406             * @event scrollto
6407             * Fires when scroll to element
6408             * @param {Roo.bootstrap.nav.Item} this
6409             * @param {Object} options
6410             * @param {Roo.EventObject} e
6411              
6412          */
6413         'scrollto': true
6414     });
6415    
6416 };
6417
6418 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6419     
6420     href: false,
6421     html: '',
6422     badge: '',
6423     icon: false,
6424     fa : false,
6425     glyphicon: false,
6426     active: false,
6427     preventDefault : false,
6428     tabId : false,
6429     tagtype : 'a',
6430     tag: 'li',
6431     disabled : false,
6432     animateRef : false,
6433     was_active : false,
6434     button_weight : '',
6435     button_outline : false,
6436     linkcls : '',
6437     navLink: false,
6438     
6439     getAutoCreate : function(){
6440          
6441         var cfg = {
6442             tag: this.tag,
6443             cls: 'nav-item'
6444         };
6445         
6446         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6447         
6448         if (this.active) {
6449             cfg.cls +=  ' active' ;
6450         }
6451         if (this.disabled) {
6452             cfg.cls += ' disabled';
6453         }
6454         
6455         // BS4 only?
6456         if (this.button_weight.length) {
6457             cfg.tag = this.href ? 'a' : 'button';
6458             cfg.html = this.html || '';
6459             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6460             if (this.href) {
6461                 cfg.href = this.href;
6462             }
6463             if (this.fa) {
6464                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6465             } else {
6466                 cfg.cls += " nav-html";
6467             }
6468             
6469             // menu .. should add dropdown-menu class - so no need for carat..
6470             
6471             if (this.badge !== '') {
6472                  
6473                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6474             }
6475             return cfg;
6476         }
6477         
6478         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6479             cfg.cn = [
6480                 {
6481                     tag: this.tagtype,
6482                     href : this.href || "#",
6483                     html: this.html || '',
6484                     cls : ''
6485                 }
6486             ];
6487             if (this.tagtype == 'a') {
6488                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6489         
6490             }
6491             if (this.icon) {
6492                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6493             } else  if (this.fa) {
6494                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495             } else if(this.glyphicon) {
6496                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6497             } else {
6498                 cfg.cn[0].cls += " nav-html";
6499             }
6500             
6501             if (this.menu) {
6502                 cfg.cn[0].html += " <span class='caret'></span>";
6503              
6504             }
6505             
6506             if (this.badge !== '') {
6507                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6508             }
6509         }
6510         
6511         
6512         
6513         return cfg;
6514     },
6515     onRender : function(ct, position)
6516     {
6517        // Roo.log("Call onRender: " + this.xtype);
6518         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6519             this.tag = 'div';
6520         }
6521         
6522         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6523         this.navLink = this.el.select('.nav-link',true).first();
6524         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6525         return ret;
6526     },
6527       
6528     
6529     initEvents: function() 
6530     {
6531         if (typeof (this.menu) != 'undefined') {
6532             this.menu.parentType = this.xtype;
6533             this.menu.triggerEl = this.el;
6534             this.menu = this.addxtype(Roo.apply({}, this.menu));
6535         }
6536         
6537         this.el.on('click', this.onClick, this);
6538         
6539         //if(this.tagtype == 'span'){
6540         //    this.el.select('span',true).on('click', this.onClick, this);
6541         //}
6542        
6543         // at this point parent should be available..
6544         this.parent().register(this);
6545     },
6546     
6547     onClick : function(e)
6548     {
6549         if (e.getTarget('.dropdown-menu-item')) {
6550             // did you click on a menu itemm.... - then don't trigger onclick..
6551             return;
6552         }
6553         
6554         if(
6555                 this.preventDefault ||
6556                                 this.href === false ||
6557                 this.href === '#' 
6558         ){
6559             //Roo.log("NavItem - prevent Default?");
6560             e.preventDefault();
6561         }
6562         
6563         if (this.disabled) {
6564             return;
6565         }
6566         
6567         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6568         if (tg && tg.transition) {
6569             Roo.log("waiting for the transitionend");
6570             return;
6571         }
6572         
6573         
6574         
6575         //Roo.log("fire event clicked");
6576         if(this.fireEvent('click', this, e) === false){
6577             return;
6578         };
6579         
6580         if(this.tagtype == 'span'){
6581             return;
6582         }
6583         
6584         //Roo.log(this.href);
6585         var ael = this.el.select('a',true).first();
6586         //Roo.log(ael);
6587         
6588         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6589             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6590             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6591                 return; // ignore... - it's a 'hash' to another page.
6592             }
6593             Roo.log("NavItem - prevent Default?");
6594             e.preventDefault();
6595             this.scrollToElement(e);
6596         }
6597         
6598         
6599         var p =  this.parent();
6600    
6601         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6602             if (typeof(p.setActiveItem) !== 'undefined') {
6603                 p.setActiveItem(this);
6604             }
6605         }
6606         
6607         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6608         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6609             // remove the collapsed menu expand...
6610             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6611         }
6612     },
6613     
6614     isActive: function () {
6615         return this.active
6616     },
6617     setActive : function(state, fire, is_was_active)
6618     {
6619         if (this.active && !state && this.navId) {
6620             this.was_active = true;
6621             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6622             if (nv) {
6623                 nv.clearWasActive(this);
6624             }
6625             
6626         }
6627         this.active = state;
6628         
6629         if (!state ) {
6630             this.el.removeClass('active');
6631             this.navLink ? this.navLink.removeClass('active') : false;
6632         } else if (!this.el.hasClass('active')) {
6633             
6634             this.el.addClass('active');
6635             if (Roo.bootstrap.version == 4 && this.navLink ) {
6636                 this.navLink.addClass('active');
6637             }
6638             
6639         }
6640         if (fire) {
6641             this.fireEvent('changed', this, state);
6642         }
6643         
6644         // show a panel if it's registered and related..
6645         
6646         if (!this.navId || !this.tabId || !state || is_was_active) {
6647             return;
6648         }
6649         
6650         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6651         if (!tg) {
6652             return;
6653         }
6654         var pan = tg.getPanelByName(this.tabId);
6655         if (!pan) {
6656             return;
6657         }
6658         // if we can not flip to new panel - go back to old nav highlight..
6659         if (false == tg.showPanel(pan)) {
6660             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6661             if (nv) {
6662                 var onav = nv.getWasActive();
6663                 if (onav) {
6664                     onav.setActive(true, false, true);
6665                 }
6666             }
6667             
6668         }
6669         
6670         
6671         
6672     },
6673      // this should not be here...
6674     setDisabled : function(state)
6675     {
6676         this.disabled = state;
6677         if (!state ) {
6678             this.el.removeClass('disabled');
6679         } else if (!this.el.hasClass('disabled')) {
6680             this.el.addClass('disabled');
6681         }
6682         
6683     },
6684     
6685     /**
6686      * Fetch the element to display the tooltip on.
6687      * @return {Roo.Element} defaults to this.el
6688      */
6689     tooltipEl : function()
6690     {
6691         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6692     },
6693     
6694     scrollToElement : function(e)
6695     {
6696         var c = document.body;
6697         
6698         /*
6699          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6700          */
6701         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6702             c = document.documentElement;
6703         }
6704         
6705         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6706         
6707         if(!target){
6708             return;
6709         }
6710
6711         var o = target.calcOffsetsTo(c);
6712         
6713         var options = {
6714             target : target,
6715             value : o[1]
6716         };
6717         
6718         this.fireEvent('scrollto', this, options, e);
6719         
6720         Roo.get(c).scrollTo('top', options.value, true);
6721         
6722         return;
6723     },
6724     /**
6725      * Set the HTML (text content) of the item
6726      * @param {string} html  content for the nav item
6727      */
6728     setHtml : function(html)
6729     {
6730         this.html = html;
6731         this.htmlEl.dom.innerHTML = html;
6732         
6733     } 
6734 });
6735  
6736
6737  /*
6738  * - LGPL
6739  *
6740  * sidebar item
6741  *
6742  *  li
6743  *    <span> icon </span>
6744  *    <span> text </span>
6745  *    <span>badge </span>
6746  */
6747
6748 /**
6749  * @class Roo.bootstrap.nav.SidebarItem
6750  * @extends Roo.bootstrap.nav.Item
6751  * Bootstrap Navbar.NavSidebarItem class
6752  * 
6753  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6754  * {Boolean} open is the menu open
6755  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6756  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6757  * {String} buttonSize (sm|md|lg)the extra classes for the button
6758  * {Boolean} showArrow show arrow next to the text (default true)
6759  * @constructor
6760  * Create a new Navbar Button
6761  * @param {Object} config The config object
6762  */
6763 Roo.bootstrap.nav.SidebarItem = function(config){
6764     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6765     this.addEvents({
6766         // raw events
6767         /**
6768          * @event click
6769          * The raw click event for the entire grid.
6770          * @param {Roo.EventObject} e
6771          */
6772         "click" : true,
6773          /**
6774             * @event changed
6775             * Fires when the active item active state changes
6776             * @param {Roo.bootstrap.nav.SidebarItem} this
6777             * @param {boolean} state the new state
6778              
6779          */
6780         'changed': true
6781     });
6782    
6783 };
6784
6785 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6786     
6787     badgeWeight : 'default',
6788     
6789     open: false,
6790     
6791     buttonView : false,
6792     
6793     buttonWeight : 'default',
6794     
6795     buttonSize : 'md',
6796     
6797     showArrow : true,
6798     
6799     getAutoCreate : function(){
6800         
6801         
6802         var a = {
6803                 tag: 'a',
6804                 href : this.href || '#',
6805                 cls: '',
6806                 html : '',
6807                 cn : []
6808         };
6809         
6810         if(this.buttonView){
6811             a = {
6812                 tag: 'button',
6813                 href : this.href || '#',
6814                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6815                 html : this.html,
6816                 cn : []
6817             };
6818         }
6819         
6820         var cfg = {
6821             tag: 'li',
6822             cls: '',
6823             cn: [ a ]
6824         };
6825         
6826         if (this.active) {
6827             cfg.cls += ' active';
6828         }
6829         
6830         if (this.disabled) {
6831             cfg.cls += ' disabled';
6832         }
6833         if (this.open) {
6834             cfg.cls += ' open x-open';
6835         }
6836         // left icon..
6837         if (this.glyphicon || this.icon) {
6838             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6839             a.cn.push({ tag : 'i', cls : c }) ;
6840         }
6841         
6842         if(!this.buttonView){
6843             var span = {
6844                 tag: 'span',
6845                 html : this.html || ''
6846             };
6847
6848             a.cn.push(span);
6849             
6850         }
6851         
6852         if (this.badge !== '') {
6853             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6854         }
6855         
6856         if (this.menu) {
6857             
6858             if(this.showArrow){
6859                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6860             }
6861             
6862             a.cls += ' dropdown-toggle treeview' ;
6863         }
6864         
6865         return cfg;
6866     },
6867     
6868     initEvents : function()
6869     { 
6870         if (typeof (this.menu) != 'undefined') {
6871             this.menu.parentType = this.xtype;
6872             this.menu.triggerEl = this.el;
6873             this.menu = this.addxtype(Roo.apply({}, this.menu));
6874         }
6875         
6876         this.el.on('click', this.onClick, this);
6877         
6878         if(this.badge !== ''){
6879             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6880         }
6881         
6882     },
6883     
6884     onClick : function(e)
6885     {
6886         if(this.disabled){
6887             e.preventDefault();
6888             return;
6889         }
6890         
6891         if(this.preventDefault){
6892             e.preventDefault();
6893         }
6894         
6895         this.fireEvent('click', this, e);
6896     },
6897     
6898     disable : function()
6899     {
6900         this.setDisabled(true);
6901     },
6902     
6903     enable : function()
6904     {
6905         this.setDisabled(false);
6906     },
6907     
6908     setDisabled : function(state)
6909     {
6910         if(this.disabled == state){
6911             return;
6912         }
6913         
6914         this.disabled = state;
6915         
6916         if (state) {
6917             this.el.addClass('disabled');
6918             return;
6919         }
6920         
6921         this.el.removeClass('disabled');
6922         
6923         return;
6924     },
6925     
6926     setActive : function(state)
6927     {
6928         if(this.active == state){
6929             return;
6930         }
6931         
6932         this.active = state;
6933         
6934         if (state) {
6935             this.el.addClass('active');
6936             return;
6937         }
6938         
6939         this.el.removeClass('active');
6940         
6941         return;
6942     },
6943     
6944     isActive: function () 
6945     {
6946         return this.active;
6947     },
6948     
6949     setBadge : function(str)
6950     {
6951         if(!this.badgeEl){
6952             return;
6953         }
6954         
6955         this.badgeEl.dom.innerHTML = str;
6956     }
6957     
6958    
6959      
6960  
6961 });
6962  
6963
6964  /*
6965  * - LGPL
6966  *
6967  * nav progress bar
6968  * 
6969  */
6970
6971 /**
6972  * @class Roo.bootstrap.nav.ProgressBar
6973  * @extends Roo.bootstrap.Component
6974  * @children Roo.bootstrap.nav.ProgressBarItem
6975  * Bootstrap NavProgressBar class
6976  * 
6977  * @constructor
6978  * Create a new nav progress bar - a bar indicating step along a process
6979  * @param {Object} config The config object
6980  */
6981
6982 Roo.bootstrap.nav.ProgressBar = function(config){
6983     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6984
6985     this.bullets = this.bullets || [];
6986    
6987 //    Roo.bootstrap.nav.ProgressBar.register(this);
6988      this.addEvents({
6989         /**
6990              * @event changed
6991              * Fires when the active item changes
6992              * @param {Roo.bootstrap.nav.ProgressBar} this
6993              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6994              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6995          */
6996         'changed': true
6997      });
6998     
6999 };
7000
7001 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7002     /**
7003      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7004      * Bullets for the Nav Progress bar for the toolbar
7005      */
7006     bullets : [],
7007     barItems : [],
7008     
7009     getAutoCreate : function()
7010     {
7011         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7012         
7013         cfg = {
7014             tag : 'div',
7015             cls : 'roo-navigation-bar-group',
7016             cn : [
7017                 {
7018                     tag : 'div',
7019                     cls : 'roo-navigation-top-bar'
7020                 },
7021                 {
7022                     tag : 'div',
7023                     cls : 'roo-navigation-bullets-bar',
7024                     cn : [
7025                         {
7026                             tag : 'ul',
7027                             cls : 'roo-navigation-bar'
7028                         }
7029                     ]
7030                 },
7031                 
7032                 {
7033                     tag : 'div',
7034                     cls : 'roo-navigation-bottom-bar'
7035                 }
7036             ]
7037             
7038         };
7039         
7040         return cfg;
7041         
7042     },
7043     
7044     initEvents: function() 
7045     {
7046         
7047     },
7048     
7049     onRender : function(ct, position) 
7050     {
7051         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7052         
7053         if(this.bullets.length){
7054             Roo.each(this.bullets, function(b){
7055                this.addItem(b);
7056             }, this);
7057         }
7058         
7059         this.format();
7060         
7061     },
7062     
7063     addItem : function(cfg)
7064     {
7065         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7066         
7067         item.parentId = this.id;
7068         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7069         
7070         if(cfg.html){
7071             var top = new Roo.bootstrap.Element({
7072                 tag : 'div',
7073                 cls : 'roo-navigation-bar-text'
7074             });
7075             
7076             var bottom = new Roo.bootstrap.Element({
7077                 tag : 'div',
7078                 cls : 'roo-navigation-bar-text'
7079             });
7080             
7081             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7082             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7083             
7084             var topText = new Roo.bootstrap.Element({
7085                 tag : 'span',
7086                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7087             });
7088             
7089             var bottomText = new Roo.bootstrap.Element({
7090                 tag : 'span',
7091                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7092             });
7093             
7094             topText.onRender(top.el, null);
7095             bottomText.onRender(bottom.el, null);
7096             
7097             item.topEl = top;
7098             item.bottomEl = bottom;
7099         }
7100         
7101         this.barItems.push(item);
7102         
7103         return item;
7104     },
7105     
7106     getActive : function()
7107     {
7108         var active = false;
7109         
7110         Roo.each(this.barItems, function(v){
7111             
7112             if (!v.isActive()) {
7113                 return;
7114             }
7115             
7116             active = v;
7117             return false;
7118             
7119         });
7120         
7121         return active;
7122     },
7123     
7124     setActiveItem : function(item)
7125     {
7126         var prev = false;
7127         
7128         Roo.each(this.barItems, function(v){
7129             if (v.rid == item.rid) {
7130                 return ;
7131             }
7132             
7133             if (v.isActive()) {
7134                 v.setActive(false);
7135                 prev = v;
7136             }
7137         });
7138
7139         item.setActive(true);
7140         
7141         this.fireEvent('changed', this, item, prev);
7142     },
7143     
7144     getBarItem: function(rid)
7145     {
7146         var ret = false;
7147         
7148         Roo.each(this.barItems, function(e) {
7149             if (e.rid != rid) {
7150                 return;
7151             }
7152             
7153             ret =  e;
7154             return false;
7155         });
7156         
7157         return ret;
7158     },
7159     
7160     indexOfItem : function(item)
7161     {
7162         var index = false;
7163         
7164         Roo.each(this.barItems, function(v, i){
7165             
7166             if (v.rid != item.rid) {
7167                 return;
7168             }
7169             
7170             index = i;
7171             return false
7172         });
7173         
7174         return index;
7175     },
7176     
7177     setActiveNext : function()
7178     {
7179         var i = this.indexOfItem(this.getActive());
7180         
7181         if (i > this.barItems.length) {
7182             return;
7183         }
7184         
7185         this.setActiveItem(this.barItems[i+1]);
7186     },
7187     
7188     setActivePrev : function()
7189     {
7190         var i = this.indexOfItem(this.getActive());
7191         
7192         if (i  < 1) {
7193             return;
7194         }
7195         
7196         this.setActiveItem(this.barItems[i-1]);
7197     },
7198     
7199     format : function()
7200     {
7201         if(!this.barItems.length){
7202             return;
7203         }
7204      
7205         var width = 100 / this.barItems.length;
7206         
7207         Roo.each(this.barItems, function(i){
7208             i.el.setStyle('width', width + '%');
7209             i.topEl.el.setStyle('width', width + '%');
7210             i.bottomEl.el.setStyle('width', width + '%');
7211         }, this);
7212         
7213     }
7214     
7215 });
7216 /*
7217  * - LGPL
7218  *
7219  * Nav Progress Item
7220  * 
7221  */
7222
7223 /**
7224  * @class Roo.bootstrap.nav.ProgressBarItem
7225  * @extends Roo.bootstrap.Component
7226  * Bootstrap NavProgressBarItem class
7227  * @cfg {String} rid the reference id
7228  * @cfg {Boolean} active (true|false) Is item active default false
7229  * @cfg {Boolean} disabled (true|false) Is item active default false
7230  * @cfg {String} html
7231  * @cfg {String} position (top|bottom) text position default bottom
7232  * @cfg {String} icon show icon instead of number
7233  * 
7234  * @constructor
7235  * Create a new NavProgressBarItem
7236  * @param {Object} config The config object
7237  */
7238 Roo.bootstrap.nav.ProgressBarItem = function(config){
7239     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7240     this.addEvents({
7241         // raw events
7242         /**
7243          * @event click
7244          * The raw click event for the entire grid.
7245          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7246          * @param {Roo.EventObject} e
7247          */
7248         "click" : true
7249     });
7250    
7251 };
7252
7253 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7254     
7255     rid : '',
7256     active : false,
7257     disabled : false,
7258     html : '',
7259     position : 'bottom',
7260     icon : false,
7261     
7262     getAutoCreate : function()
7263     {
7264         var iconCls = 'roo-navigation-bar-item-icon';
7265         
7266         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7267         
7268         var cfg = {
7269             tag: 'li',
7270             cls: 'roo-navigation-bar-item',
7271             cn : [
7272                 {
7273                     tag : 'i',
7274                     cls : iconCls
7275                 }
7276             ]
7277         };
7278         
7279         if(this.active){
7280             cfg.cls += ' active';
7281         }
7282         if(this.disabled){
7283             cfg.cls += ' disabled';
7284         }
7285         
7286         return cfg;
7287     },
7288     
7289     disable : function()
7290     {
7291         this.setDisabled(true);
7292     },
7293     
7294     enable : function()
7295     {
7296         this.setDisabled(false);
7297     },
7298     
7299     initEvents: function() 
7300     {
7301         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7302         
7303         this.iconEl.on('click', this.onClick, this);
7304     },
7305     
7306     onClick : function(e)
7307     {
7308         e.preventDefault();
7309         
7310         if(this.disabled){
7311             return;
7312         }
7313         
7314         if(this.fireEvent('click', this, e) === false){
7315             return;
7316         };
7317         
7318         this.parent().setActiveItem(this);
7319     },
7320     
7321     isActive: function () 
7322     {
7323         return this.active;
7324     },
7325     
7326     setActive : function(state)
7327     {
7328         if(this.active == state){
7329             return;
7330         }
7331         
7332         this.active = state;
7333         
7334         if (state) {
7335             this.el.addClass('active');
7336             return;
7337         }
7338         
7339         this.el.removeClass('active');
7340         
7341         return;
7342     },
7343     
7344     setDisabled : function(state)
7345     {
7346         if(this.disabled == state){
7347             return;
7348         }
7349         
7350         this.disabled = state;
7351         
7352         if (state) {
7353             this.el.addClass('disabled');
7354             return;
7355         }
7356         
7357         this.el.removeClass('disabled');
7358     },
7359     
7360     tooltipEl : function()
7361     {
7362         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7363     }
7364 });
7365  
7366
7367  /*
7368  * - LGPL
7369  *
7370  *  Breadcrumb Nav
7371  * 
7372  */
7373 Roo.namespace('Roo.bootstrap.breadcrumb');
7374
7375
7376 /**
7377  * @class Roo.bootstrap.breadcrumb.Nav
7378  * @extends Roo.bootstrap.Component
7379  * Bootstrap Breadcrumb Nav Class
7380  *  
7381  * @children Roo.bootstrap.breadcrumb.Item
7382  * 
7383  * @constructor
7384  * Create a new breadcrumb.Nav
7385  * @param {Object} config The config object
7386  */
7387
7388
7389 Roo.bootstrap.breadcrumb.Nav = function(config){
7390     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7391     
7392     
7393 };
7394
7395 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7396     
7397     getAutoCreate : function()
7398     {
7399
7400         var cfg = {
7401             tag: 'nav',
7402             cn : [
7403                 {
7404                     tag : 'ol',
7405                     cls : 'breadcrumb'
7406                 }
7407             ]
7408             
7409         };
7410           
7411         return cfg;
7412     },
7413     
7414     initEvents: function()
7415     {
7416         this.olEl = this.el.select('ol',true).first();    
7417     },
7418     getChildContainer : function()
7419     {
7420         return this.olEl;  
7421     }
7422     
7423 });
7424
7425  /*
7426  * - LGPL
7427  *
7428  *  Breadcrumb Item
7429  * 
7430  */
7431
7432
7433 /**
7434  * @class Roo.bootstrap.breadcrumb.Nav
7435  * @extends Roo.bootstrap.Component
7436  * @children Roo.bootstrap.Component
7437  * @parent Roo.bootstrap.breadcrumb.Nav
7438  * Bootstrap Breadcrumb Nav Class
7439  *  
7440  * 
7441  * @cfg {String} html the content of the link.
7442  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7443  * @cfg {Boolean} active is it active
7444
7445  * 
7446  * @constructor
7447  * Create a new breadcrumb.Nav
7448  * @param {Object} config The config object
7449  */
7450
7451 Roo.bootstrap.breadcrumb.Item = function(config){
7452     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7453     this.addEvents({
7454         // img events
7455         /**
7456          * @event click
7457          * The img click event for the img.
7458          * @param {Roo.EventObject} e
7459          */
7460         "click" : true
7461     });
7462     
7463 };
7464
7465 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7466     
7467     href: false,
7468     html : '',
7469     
7470     getAutoCreate : function()
7471     {
7472
7473         var cfg = {
7474             tag: 'li',
7475             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7476         };
7477         if (this.href !== false) {
7478             cfg.cn = [{
7479                 tag : 'a',
7480                 href : this.href,
7481                 html : this.html
7482             }];
7483         } else {
7484             cfg.html = this.html;
7485         }
7486         
7487         return cfg;
7488     },
7489     
7490     initEvents: function()
7491     {
7492         if (this.href) {
7493             this.el.select('a', true).first().on('click',this.onClick, this)
7494         }
7495         
7496     },
7497     onClick : function(e)
7498     {
7499         e.preventDefault();
7500         this.fireEvent('click',this,  e);
7501     }
7502     
7503 });
7504
7505  /*
7506  * - LGPL
7507  *
7508  * row
7509  * 
7510  */
7511
7512 /**
7513  * @class Roo.bootstrap.Row
7514  * @extends Roo.bootstrap.Component
7515  * @children Roo.bootstrap.Component
7516  * Bootstrap Row class (contains columns...)
7517  * 
7518  * @constructor
7519  * Create a new Row
7520  * @param {Object} config The config object
7521  */
7522
7523 Roo.bootstrap.Row = function(config){
7524     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7525 };
7526
7527 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7528     
7529     getAutoCreate : function(){
7530        return {
7531             cls: 'row clearfix'
7532        };
7533     }
7534     
7535     
7536 });
7537
7538  
7539
7540  /*
7541  * - LGPL
7542  *
7543  * pagination
7544  * 
7545  */
7546
7547 /**
7548  * @class Roo.bootstrap.Pagination
7549  * @extends Roo.bootstrap.Component
7550  * @children Roo.bootstrap.Pagination
7551  * Bootstrap Pagination class
7552  * 
7553  * @cfg {String} size (xs|sm|md|lg|xl)
7554  * @cfg {Boolean} inverse 
7555  * 
7556  * @constructor
7557  * Create a new Pagination
7558  * @param {Object} config The config object
7559  */
7560
7561 Roo.bootstrap.Pagination = function(config){
7562     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7563 };
7564
7565 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7566     
7567     cls: false,
7568     size: false,
7569     inverse: false,
7570     
7571     getAutoCreate : function(){
7572         var cfg = {
7573             tag: 'ul',
7574                 cls: 'pagination'
7575         };
7576         if (this.inverse) {
7577             cfg.cls += ' inverse';
7578         }
7579         if (this.html) {
7580             cfg.html=this.html;
7581         }
7582         if (this.cls) {
7583             cfg.cls += " " + this.cls;
7584         }
7585         return cfg;
7586     }
7587    
7588 });
7589
7590  
7591
7592  /*
7593  * - LGPL
7594  *
7595  * Pagination item
7596  * 
7597  */
7598
7599
7600 /**
7601  * @class Roo.bootstrap.PaginationItem
7602  * @extends Roo.bootstrap.Component
7603  * Bootstrap PaginationItem class
7604  * @cfg {String} html text
7605  * @cfg {String} href the link
7606  * @cfg {Boolean} preventDefault (true | false) default true
7607  * @cfg {Boolean} active (true | false) default false
7608  * @cfg {Boolean} disabled default false
7609  * 
7610  * 
7611  * @constructor
7612  * Create a new PaginationItem
7613  * @param {Object} config The config object
7614  */
7615
7616
7617 Roo.bootstrap.PaginationItem = function(config){
7618     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7619     this.addEvents({
7620         // raw events
7621         /**
7622          * @event click
7623          * The raw click event for the entire grid.
7624          * @param {Roo.EventObject} e
7625          */
7626         "click" : true
7627     });
7628 };
7629
7630 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7631     
7632     href : false,
7633     html : false,
7634     preventDefault: true,
7635     active : false,
7636     cls : false,
7637     disabled: false,
7638     
7639     getAutoCreate : function(){
7640         var cfg= {
7641             tag: 'li',
7642             cn: [
7643                 {
7644                     tag : 'a',
7645                     href : this.href ? this.href : '#',
7646                     html : this.html ? this.html : ''
7647                 }
7648             ]
7649         };
7650         
7651         if(this.cls){
7652             cfg.cls = this.cls;
7653         }
7654         
7655         if(this.disabled){
7656             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7657         }
7658         
7659         if(this.active){
7660             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7661         }
7662         
7663         return cfg;
7664     },
7665     
7666     initEvents: function() {
7667         
7668         this.el.on('click', this.onClick, this);
7669         
7670     },
7671     onClick : function(e)
7672     {
7673         Roo.log('PaginationItem on click ');
7674         if(this.preventDefault){
7675             e.preventDefault();
7676         }
7677         
7678         if(this.disabled){
7679             return;
7680         }
7681         
7682         this.fireEvent('click', this, e);
7683     }
7684    
7685 });
7686
7687  
7688
7689  /*
7690  * - LGPL
7691  *
7692  * slider
7693  * 
7694  */
7695
7696
7697 /**
7698  * @class Roo.bootstrap.Slider
7699  * @extends Roo.bootstrap.Component
7700  * Bootstrap Slider class
7701  *    
7702  * @constructor
7703  * Create a new Slider
7704  * @param {Object} config The config object
7705  */
7706
7707 Roo.bootstrap.Slider = function(config){
7708     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7709 };
7710
7711 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7712     
7713     getAutoCreate : function(){
7714         
7715         var cfg = {
7716             tag: 'div',
7717             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7718             cn: [
7719                 {
7720                     tag: 'a',
7721                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7722                 }
7723             ]
7724         };
7725         
7726         return cfg;
7727     }
7728    
7729 });
7730
7731  /*
7732  * Based on:
7733  * Ext JS Library 1.1.1
7734  * Copyright(c) 2006-2007, Ext JS, LLC.
7735  *
7736  * Originally Released Under LGPL - original licence link has changed is not relivant.
7737  *
7738  * Fork - LGPL
7739  * <script type="text/javascript">
7740  */
7741  /**
7742  * @extends Roo.dd.DDProxy
7743  * @class Roo.grid.SplitDragZone
7744  * Support for Column Header resizing
7745  * @constructor
7746  * @param {Object} config
7747  */
7748 // private
7749 // This is a support class used internally by the Grid components
7750 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7751     this.grid = grid;
7752     this.view = grid.getView();
7753     this.proxy = this.view.resizeProxy;
7754     Roo.grid.SplitDragZone.superclass.constructor.call(
7755         this,
7756         hd, // ID
7757         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7758         {  // CONFIG
7759             dragElId : Roo.id(this.proxy.dom),
7760             resizeFrame:false
7761         }
7762     );
7763     
7764     this.setHandleElId(Roo.id(hd));
7765     if (hd2 !== false) {
7766         this.setOuterHandleElId(Roo.id(hd2));
7767     }
7768     
7769     this.scroll = false;
7770 };
7771 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7772     fly: Roo.Element.fly,
7773
7774     b4StartDrag : function(x, y){
7775         this.view.headersDisabled = true;
7776         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7777                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7778         );
7779         this.proxy.setHeight(h);
7780         
7781         // for old system colWidth really stored the actual width?
7782         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7783         // which in reality did not work.. - it worked only for fixed sizes
7784         // for resizable we need to use actual sizes.
7785         var w = this.cm.getColumnWidth(this.cellIndex);
7786         if (!this.view.mainWrap) {
7787             // bootstrap.
7788             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7789         }
7790         
7791         
7792         
7793         // this was w-this.grid.minColumnWidth;
7794         // doesnt really make sense? - w = thie curren width or the rendered one?
7795         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7796         this.resetConstraints();
7797         this.setXConstraint(minw, 1000);
7798         this.setYConstraint(0, 0);
7799         this.minX = x - minw;
7800         this.maxX = x + 1000;
7801         this.startPos = x;
7802         if (!this.view.mainWrap) { // this is Bootstrap code..
7803             this.getDragEl().style.display='block';
7804         }
7805         
7806         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7807     },
7808
7809
7810     handleMouseDown : function(e){
7811         ev = Roo.EventObject.setEvent(e);
7812         var t = this.fly(ev.getTarget());
7813         if(t.hasClass("x-grid-split")){
7814             this.cellIndex = this.view.getCellIndex(t.dom);
7815             this.split = t.dom;
7816             this.cm = this.grid.colModel;
7817             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7818                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7819             }
7820         }
7821     },
7822
7823     endDrag : function(e){
7824         this.view.headersDisabled = false;
7825         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7826         var diff = endX - this.startPos;
7827         // 
7828         var w = this.cm.getColumnWidth(this.cellIndex);
7829         if (!this.view.mainWrap) {
7830             w = 0;
7831         }
7832         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7833     },
7834
7835     autoOffset : function(){
7836         this.setDelta(0,0);
7837     }
7838 });/*
7839  * Based on:
7840  * Ext JS Library 1.1.1
7841  * Copyright(c) 2006-2007, Ext JS, LLC.
7842  *
7843  * Originally Released Under LGPL - original licence link has changed is not relivant.
7844  *
7845  * Fork - LGPL
7846  * <script type="text/javascript">
7847  */
7848
7849 /**
7850  * @class Roo.grid.AbstractSelectionModel
7851  * @extends Roo.util.Observable
7852  * @abstract
7853  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7854  * implemented by descendant classes.  This class should not be directly instantiated.
7855  * @constructor
7856  */
7857 Roo.grid.AbstractSelectionModel = function(){
7858     this.locked = false;
7859     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7860 };
7861
7862 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7863     /** @ignore Called by the grid automatically. Do not call directly. */
7864     init : function(grid){
7865         this.grid = grid;
7866         this.initEvents();
7867     },
7868
7869     /**
7870      * Locks the selections.
7871      */
7872     lock : function(){
7873         this.locked = true;
7874     },
7875
7876     /**
7877      * Unlocks the selections.
7878      */
7879     unlock : function(){
7880         this.locked = false;
7881     },
7882
7883     /**
7884      * Returns true if the selections are locked.
7885      * @return {Boolean}
7886      */
7887     isLocked : function(){
7888         return this.locked;
7889     }
7890 });/*
7891  * Based on:
7892  * Ext JS Library 1.1.1
7893  * Copyright(c) 2006-2007, Ext JS, LLC.
7894  *
7895  * Originally Released Under LGPL - original licence link has changed is not relivant.
7896  *
7897  * Fork - LGPL
7898  * <script type="text/javascript">
7899  */
7900 /**
7901  * @extends Roo.grid.AbstractSelectionModel
7902  * @class Roo.grid.RowSelectionModel
7903  * The default SelectionModel used by {@link Roo.grid.Grid}.
7904  * It supports multiple selections and keyboard selection/navigation. 
7905  * @constructor
7906  * @param {Object} config
7907  */
7908 Roo.grid.RowSelectionModel = function(config){
7909     Roo.apply(this, config);
7910     this.selections = new Roo.util.MixedCollection(false, function(o){
7911         return o.id;
7912     });
7913
7914     this.last = false;
7915     this.lastActive = false;
7916
7917     this.addEvents({
7918         /**
7919         * @event selectionchange
7920         * Fires when the selection changes
7921         * @param {SelectionModel} this
7922         */
7923        "selectionchange" : true,
7924        /**
7925         * @event afterselectionchange
7926         * Fires after the selection changes (eg. by key press or clicking)
7927         * @param {SelectionModel} this
7928         */
7929        "afterselectionchange" : true,
7930        /**
7931         * @event beforerowselect
7932         * Fires when a row is selected being selected, return false to cancel.
7933         * @param {SelectionModel} this
7934         * @param {Number} rowIndex The selected index
7935         * @param {Boolean} keepExisting False if other selections will be cleared
7936         */
7937        "beforerowselect" : true,
7938        /**
7939         * @event rowselect
7940         * Fires when a row is selected.
7941         * @param {SelectionModel} this
7942         * @param {Number} rowIndex The selected index
7943         * @param {Roo.data.Record} r The record
7944         */
7945        "rowselect" : true,
7946        /**
7947         * @event rowdeselect
7948         * Fires when a row is deselected.
7949         * @param {SelectionModel} this
7950         * @param {Number} rowIndex The selected index
7951         */
7952         "rowdeselect" : true
7953     });
7954     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7955     this.locked = false;
7956 };
7957
7958 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7959     /**
7960      * @cfg {Boolean} singleSelect
7961      * True to allow selection of only one row at a time (defaults to false)
7962      */
7963     singleSelect : false,
7964
7965     // private
7966     initEvents : function(){
7967
7968         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7969             this.grid.on("mousedown", this.handleMouseDown, this);
7970         }else{ // allow click to work like normal
7971             this.grid.on("rowclick", this.handleDragableRowClick, this);
7972         }
7973         // bootstrap does not have a view..
7974         var view = this.grid.view ? this.grid.view : this.grid;
7975         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7976             "up" : function(e){
7977                 if(!e.shiftKey){
7978                     this.selectPrevious(e.shiftKey);
7979                 }else if(this.last !== false && this.lastActive !== false){
7980                     var last = this.last;
7981                     this.selectRange(this.last,  this.lastActive-1);
7982                     view.focusRow(this.lastActive);
7983                     if(last !== false){
7984                         this.last = last;
7985                     }
7986                 }else{
7987                     this.selectFirstRow();
7988                 }
7989                 this.fireEvent("afterselectionchange", this);
7990             },
7991             "down" : function(e){
7992                 if(!e.shiftKey){
7993                     this.selectNext(e.shiftKey);
7994                 }else if(this.last !== false && this.lastActive !== false){
7995                     var last = this.last;
7996                     this.selectRange(this.last,  this.lastActive+1);
7997                     view.focusRow(this.lastActive);
7998                     if(last !== false){
7999                         this.last = last;
8000                     }
8001                 }else{
8002                     this.selectFirstRow();
8003                 }
8004                 this.fireEvent("afterselectionchange", this);
8005             },
8006             scope: this
8007         });
8008
8009          
8010         view.on("refresh", this.onRefresh, this);
8011         view.on("rowupdated", this.onRowUpdated, this);
8012         view.on("rowremoved", this.onRemove, this);
8013     },
8014
8015     // private
8016     onRefresh : function(){
8017         var ds = this.grid.ds, i, v = this.grid.view;
8018         var s = this.selections;
8019         s.each(function(r){
8020             if((i = ds.indexOfId(r.id)) != -1){
8021                 v.onRowSelect(i);
8022                 s.add(ds.getAt(i)); // updating the selection relate data
8023             }else{
8024                 s.remove(r);
8025             }
8026         });
8027     },
8028
8029     // private
8030     onRemove : function(v, index, r){
8031         this.selections.remove(r);
8032     },
8033
8034     // private
8035     onRowUpdated : function(v, index, r){
8036         if(this.isSelected(r)){
8037             v.onRowSelect(index);
8038         }
8039     },
8040
8041     /**
8042      * Select records.
8043      * @param {Array} records The records to select
8044      * @param {Boolean} keepExisting (optional) True to keep existing selections
8045      */
8046     selectRecords : function(records, keepExisting){
8047         if(!keepExisting){
8048             this.clearSelections();
8049         }
8050         var ds = this.grid.ds;
8051         for(var i = 0, len = records.length; i < len; i++){
8052             this.selectRow(ds.indexOf(records[i]), true);
8053         }
8054     },
8055
8056     /**
8057      * Gets the number of selected rows.
8058      * @return {Number}
8059      */
8060     getCount : function(){
8061         return this.selections.length;
8062     },
8063
8064     /**
8065      * Selects the first row in the grid.
8066      */
8067     selectFirstRow : function(){
8068         this.selectRow(0);
8069     },
8070
8071     /**
8072      * Select the last row.
8073      * @param {Boolean} keepExisting (optional) True to keep existing selections
8074      */
8075     selectLastRow : function(keepExisting){
8076         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8077     },
8078
8079     /**
8080      * Selects the row immediately following the last selected row.
8081      * @param {Boolean} keepExisting (optional) True to keep existing selections
8082      */
8083     selectNext : function(keepExisting){
8084         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8085             this.selectRow(this.last+1, keepExisting);
8086             var view = this.grid.view ? this.grid.view : this.grid;
8087             view.focusRow(this.last);
8088         }
8089     },
8090
8091     /**
8092      * Selects the row that precedes the last selected row.
8093      * @param {Boolean} keepExisting (optional) True to keep existing selections
8094      */
8095     selectPrevious : function(keepExisting){
8096         if(this.last){
8097             this.selectRow(this.last-1, keepExisting);
8098             var view = this.grid.view ? this.grid.view : this.grid;
8099             view.focusRow(this.last);
8100         }
8101     },
8102
8103     /**
8104      * Returns the selected records
8105      * @return {Array} Array of selected records
8106      */
8107     getSelections : function(){
8108         return [].concat(this.selections.items);
8109     },
8110
8111     /**
8112      * Returns the first selected record.
8113      * @return {Record}
8114      */
8115     getSelected : function(){
8116         return this.selections.itemAt(0);
8117     },
8118
8119
8120     /**
8121      * Clears all selections.
8122      */
8123     clearSelections : function(fast){
8124         if(this.locked) {
8125             return;
8126         }
8127         if(fast !== true){
8128             var ds = this.grid.ds;
8129             var s = this.selections;
8130             s.each(function(r){
8131                 this.deselectRow(ds.indexOfId(r.id));
8132             }, this);
8133             s.clear();
8134         }else{
8135             this.selections.clear();
8136         }
8137         this.last = false;
8138     },
8139
8140
8141     /**
8142      * Selects all rows.
8143      */
8144     selectAll : function(){
8145         if(this.locked) {
8146             return;
8147         }
8148         this.selections.clear();
8149         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8150             this.selectRow(i, true);
8151         }
8152     },
8153
8154     /**
8155      * Returns True if there is a selection.
8156      * @return {Boolean}
8157      */
8158     hasSelection : function(){
8159         return this.selections.length > 0;
8160     },
8161
8162     /**
8163      * Returns True if the specified row is selected.
8164      * @param {Number/Record} record The record or index of the record to check
8165      * @return {Boolean}
8166      */
8167     isSelected : function(index){
8168         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8169         return (r && this.selections.key(r.id) ? true : false);
8170     },
8171
8172     /**
8173      * Returns True if the specified record id is selected.
8174      * @param {String} id The id of record to check
8175      * @return {Boolean}
8176      */
8177     isIdSelected : function(id){
8178         return (this.selections.key(id) ? true : false);
8179     },
8180
8181     // private
8182     handleMouseDown : function(e, t)
8183     {
8184         var view = this.grid.view ? this.grid.view : this.grid;
8185         var rowIndex;
8186         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8187             return;
8188         };
8189         if(e.shiftKey && this.last !== false){
8190             var last = this.last;
8191             this.selectRange(last, rowIndex, e.ctrlKey);
8192             this.last = last; // reset the last
8193             view.focusRow(rowIndex);
8194         }else{
8195             var isSelected = this.isSelected(rowIndex);
8196             if(e.button !== 0 && isSelected){
8197                 view.focusRow(rowIndex);
8198             }else if(e.ctrlKey && isSelected){
8199                 this.deselectRow(rowIndex);
8200             }else if(!isSelected){
8201                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8202                 view.focusRow(rowIndex);
8203             }
8204         }
8205         this.fireEvent("afterselectionchange", this);
8206     },
8207     // private
8208     handleDragableRowClick :  function(grid, rowIndex, e) 
8209     {
8210         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8211             this.selectRow(rowIndex, false);
8212             var view = this.grid.view ? this.grid.view : this.grid;
8213             view.focusRow(rowIndex);
8214              this.fireEvent("afterselectionchange", this);
8215         }
8216     },
8217     
8218     /**
8219      * Selects multiple rows.
8220      * @param {Array} rows Array of the indexes of the row to select
8221      * @param {Boolean} keepExisting (optional) True to keep existing selections
8222      */
8223     selectRows : function(rows, keepExisting){
8224         if(!keepExisting){
8225             this.clearSelections();
8226         }
8227         for(var i = 0, len = rows.length; i < len; i++){
8228             this.selectRow(rows[i], true);
8229         }
8230     },
8231
8232     /**
8233      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8234      * @param {Number} startRow The index of the first row in the range
8235      * @param {Number} endRow The index of the last row in the range
8236      * @param {Boolean} keepExisting (optional) True to retain existing selections
8237      */
8238     selectRange : function(startRow, endRow, keepExisting){
8239         if(this.locked) {
8240             return;
8241         }
8242         if(!keepExisting){
8243             this.clearSelections();
8244         }
8245         if(startRow <= endRow){
8246             for(var i = startRow; i <= endRow; i++){
8247                 this.selectRow(i, true);
8248             }
8249         }else{
8250             for(var i = startRow; i >= endRow; i--){
8251                 this.selectRow(i, true);
8252             }
8253         }
8254     },
8255
8256     /**
8257      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8258      * @param {Number} startRow The index of the first row in the range
8259      * @param {Number} endRow The index of the last row in the range
8260      */
8261     deselectRange : function(startRow, endRow, preventViewNotify){
8262         if(this.locked) {
8263             return;
8264         }
8265         for(var i = startRow; i <= endRow; i++){
8266             this.deselectRow(i, preventViewNotify);
8267         }
8268     },
8269
8270     /**
8271      * Selects a row.
8272      * @param {Number} row The index of the row to select
8273      * @param {Boolean} keepExisting (optional) True to keep existing selections
8274      */
8275     selectRow : function(index, keepExisting, preventViewNotify){
8276         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8277             return;
8278         }
8279         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8280             if(!keepExisting || this.singleSelect){
8281                 this.clearSelections();
8282             }
8283             var r = this.grid.ds.getAt(index);
8284             this.selections.add(r);
8285             this.last = this.lastActive = index;
8286             if(!preventViewNotify){
8287                 var view = this.grid.view ? this.grid.view : this.grid;
8288                 view.onRowSelect(index);
8289             }
8290             this.fireEvent("rowselect", this, index, r);
8291             this.fireEvent("selectionchange", this);
8292         }
8293     },
8294
8295     /**
8296      * Deselects a row.
8297      * @param {Number} row The index of the row to deselect
8298      */
8299     deselectRow : function(index, preventViewNotify){
8300         if(this.locked) {
8301             return;
8302         }
8303         if(this.last == index){
8304             this.last = false;
8305         }
8306         if(this.lastActive == index){
8307             this.lastActive = false;
8308         }
8309         var r = this.grid.ds.getAt(index);
8310         this.selections.remove(r);
8311         if(!preventViewNotify){
8312             var view = this.grid.view ? this.grid.view : this.grid;
8313             view.onRowDeselect(index);
8314         }
8315         this.fireEvent("rowdeselect", this, index);
8316         this.fireEvent("selectionchange", this);
8317     },
8318
8319     // private
8320     restoreLast : function(){
8321         if(this._last){
8322             this.last = this._last;
8323         }
8324     },
8325
8326     // private
8327     acceptsNav : function(row, col, cm){
8328         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8329     },
8330
8331     // private
8332     onEditorKey : function(field, e){
8333         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8334         if(k == e.TAB){
8335             e.stopEvent();
8336             ed.completeEdit();
8337             if(e.shiftKey){
8338                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8339             }else{
8340                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8341             }
8342         }else if(k == e.ENTER && !e.ctrlKey){
8343             e.stopEvent();
8344             ed.completeEdit();
8345             if(e.shiftKey){
8346                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8347             }else{
8348                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8349             }
8350         }else if(k == e.ESC){
8351             ed.cancelEdit();
8352         }
8353         if(newCell){
8354             g.startEditing(newCell[0], newCell[1]);
8355         }
8356     }
8357 });/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367  
8368
8369 /**
8370  * @class Roo.grid.ColumnModel
8371  * @extends Roo.util.Observable
8372  * This is the default implementation of a ColumnModel used by the Grid. It defines
8373  * the columns in the grid.
8374  * <br>Usage:<br>
8375  <pre><code>
8376  var colModel = new Roo.grid.ColumnModel([
8377         {header: "Ticker", width: 60, sortable: true, locked: true},
8378         {header: "Company Name", width: 150, sortable: true},
8379         {header: "Market Cap.", width: 100, sortable: true},
8380         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8381         {header: "Employees", width: 100, sortable: true, resizable: false}
8382  ]);
8383  </code></pre>
8384  * <p>
8385  
8386  * The config options listed for this class are options which may appear in each
8387  * individual column definition.
8388  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8389  * @constructor
8390  * @param {Object} config An Array of column config objects. See this class's
8391  * config objects for details.
8392 */
8393 Roo.grid.ColumnModel = function(config){
8394         /**
8395      * The config passed into the constructor
8396      */
8397     this.config = []; //config;
8398     this.lookup = {};
8399
8400     // if no id, create one
8401     // if the column does not have a dataIndex mapping,
8402     // map it to the order it is in the config
8403     for(var i = 0, len = config.length; i < len; i++){
8404         this.addColumn(config[i]);
8405         
8406     }
8407
8408     /**
8409      * The width of columns which have no width specified (defaults to 100)
8410      * @type Number
8411      */
8412     this.defaultWidth = 100;
8413
8414     /**
8415      * Default sortable of columns which have no sortable specified (defaults to false)
8416      * @type Boolean
8417      */
8418     this.defaultSortable = false;
8419
8420     this.addEvents({
8421         /**
8422              * @event widthchange
8423              * Fires when the width of a column changes.
8424              * @param {ColumnModel} this
8425              * @param {Number} columnIndex The column index
8426              * @param {Number} newWidth The new width
8427              */
8428             "widthchange": true,
8429         /**
8430              * @event headerchange
8431              * Fires when the text of a header changes.
8432              * @param {ColumnModel} this
8433              * @param {Number} columnIndex The column index
8434              * @param {Number} newText The new header text
8435              */
8436             "headerchange": true,
8437         /**
8438              * @event hiddenchange
8439              * Fires when a column is hidden or "unhidden".
8440              * @param {ColumnModel} this
8441              * @param {Number} columnIndex The column index
8442              * @param {Boolean} hidden true if hidden, false otherwise
8443              */
8444             "hiddenchange": true,
8445             /**
8446          * @event columnmoved
8447          * Fires when a column is moved.
8448          * @param {ColumnModel} this
8449          * @param {Number} oldIndex
8450          * @param {Number} newIndex
8451          */
8452         "columnmoved" : true,
8453         /**
8454          * @event columlockchange
8455          * Fires when a column's locked state is changed
8456          * @param {ColumnModel} this
8457          * @param {Number} colIndex
8458          * @param {Boolean} locked true if locked
8459          */
8460         "columnlockchange" : true
8461     });
8462     Roo.grid.ColumnModel.superclass.constructor.call(this);
8463 };
8464 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8465     /**
8466      * @cfg {String} header [required] The header text to display in the Grid view.
8467      */
8468         /**
8469      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8470      */
8471         /**
8472      * @cfg {String} smHeader Header at Bootsrap Small width
8473      */
8474         /**
8475      * @cfg {String} mdHeader Header at Bootsrap Medium width
8476      */
8477         /**
8478      * @cfg {String} lgHeader Header at Bootsrap Large width
8479      */
8480         /**
8481      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8482      */
8483     /**
8484      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8485      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8486      * specified, the column's index is used as an index into the Record's data Array.
8487      */
8488     /**
8489      * @cfg {Number} width  The initial width in pixels of the column. Using this
8490      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8491      */
8492     /**
8493      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8494      * Defaults to the value of the {@link #defaultSortable} property.
8495      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8496      */
8497     /**
8498      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8499      */
8500     /**
8501      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8502      */
8503     /**
8504      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8505      */
8506     /**
8507      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8508      */
8509     /**
8510      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8511      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8512      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8513      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8514      */
8515        /**
8516      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8517      */
8518     /**
8519      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8520      */
8521     /**
8522      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8523      */
8524     /**
8525      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
8526      */
8527     /**
8528      * @cfg {String} tooltip mouse over tooltip text
8529      */
8530     /**
8531      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8532      */
8533     /**
8534      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8535      */
8536     /**
8537      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8538      */
8539     /**
8540      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8541      */
8542         /**
8543      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8544      */
8545     /**
8546      * Returns the id of the column at the specified index.
8547      * @param {Number} index The column index
8548      * @return {String} the id
8549      */
8550     getColumnId : function(index){
8551         return this.config[index].id;
8552     },
8553
8554     /**
8555      * Returns the column for a specified id.
8556      * @param {String} id The column id
8557      * @return {Object} the column
8558      */
8559     getColumnById : function(id){
8560         return this.lookup[id];
8561     },
8562
8563     
8564     /**
8565      * Returns the column Object for a specified dataIndex.
8566      * @param {String} dataIndex The column dataIndex
8567      * @return {Object|Boolean} the column or false if not found
8568      */
8569     getColumnByDataIndex: function(dataIndex){
8570         var index = this.findColumnIndex(dataIndex);
8571         return index > -1 ? this.config[index] : false;
8572     },
8573     
8574     /**
8575      * Returns the index for a specified column id.
8576      * @param {String} id The column id
8577      * @return {Number} the index, or -1 if not found
8578      */
8579     getIndexById : function(id){
8580         for(var i = 0, len = this.config.length; i < len; i++){
8581             if(this.config[i].id == id){
8582                 return i;
8583             }
8584         }
8585         return -1;
8586     },
8587     
8588     /**
8589      * Returns the index for a specified column dataIndex.
8590      * @param {String} dataIndex The column dataIndex
8591      * @return {Number} the index, or -1 if not found
8592      */
8593     
8594     findColumnIndex : function(dataIndex){
8595         for(var i = 0, len = this.config.length; i < len; i++){
8596             if(this.config[i].dataIndex == dataIndex){
8597                 return i;
8598             }
8599         }
8600         return -1;
8601     },
8602     
8603     
8604     moveColumn : function(oldIndex, newIndex){
8605         var c = this.config[oldIndex];
8606         this.config.splice(oldIndex, 1);
8607         this.config.splice(newIndex, 0, c);
8608         this.dataMap = null;
8609         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8610     },
8611
8612     isLocked : function(colIndex){
8613         return this.config[colIndex].locked === true;
8614     },
8615
8616     setLocked : function(colIndex, value, suppressEvent){
8617         if(this.isLocked(colIndex) == value){
8618             return;
8619         }
8620         this.config[colIndex].locked = value;
8621         if(!suppressEvent){
8622             this.fireEvent("columnlockchange", this, colIndex, value);
8623         }
8624     },
8625
8626     getTotalLockedWidth : function(){
8627         var totalWidth = 0;
8628         for(var i = 0; i < this.config.length; i++){
8629             if(this.isLocked(i) && !this.isHidden(i)){
8630                 this.totalWidth += this.getColumnWidth(i);
8631             }
8632         }
8633         return totalWidth;
8634     },
8635
8636     getLockedCount : function(){
8637         for(var i = 0, len = this.config.length; i < len; i++){
8638             if(!this.isLocked(i)){
8639                 return i;
8640             }
8641         }
8642         
8643         return this.config.length;
8644     },
8645
8646     /**
8647      * Returns the number of columns.
8648      * @return {Number}
8649      */
8650     getColumnCount : function(visibleOnly){
8651         if(visibleOnly === true){
8652             var c = 0;
8653             for(var i = 0, len = this.config.length; i < len; i++){
8654                 if(!this.isHidden(i)){
8655                     c++;
8656                 }
8657             }
8658             return c;
8659         }
8660         return this.config.length;
8661     },
8662
8663     /**
8664      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8665      * @param {Function} fn
8666      * @param {Object} scope (optional)
8667      * @return {Array} result
8668      */
8669     getColumnsBy : function(fn, scope){
8670         var r = [];
8671         for(var i = 0, len = this.config.length; i < len; i++){
8672             var c = this.config[i];
8673             if(fn.call(scope||this, c, i) === true){
8674                 r[r.length] = c;
8675             }
8676         }
8677         return r;
8678     },
8679
8680     /**
8681      * Returns true if the specified column is sortable.
8682      * @param {Number} col The column index
8683      * @return {Boolean}
8684      */
8685     isSortable : function(col){
8686         if(typeof this.config[col].sortable == "undefined"){
8687             return this.defaultSortable;
8688         }
8689         return this.config[col].sortable;
8690     },
8691
8692     /**
8693      * Returns the rendering (formatting) function defined for the column.
8694      * @param {Number} col The column index.
8695      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8696      */
8697     getRenderer : function(col){
8698         if(!this.config[col].renderer){
8699             return Roo.grid.ColumnModel.defaultRenderer;
8700         }
8701         return this.config[col].renderer;
8702     },
8703
8704     /**
8705      * Sets the rendering (formatting) function for a column.
8706      * @param {Number} col The column index
8707      * @param {Function} fn The function to use to process the cell's raw data
8708      * to return HTML markup for the grid view. The render function is called with
8709      * the following parameters:<ul>
8710      * <li>Data value.</li>
8711      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8712      * <li>css A CSS style string to apply to the table cell.</li>
8713      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8714      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8715      * <li>Row index</li>
8716      * <li>Column index</li>
8717      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8718      */
8719     setRenderer : function(col, fn){
8720         this.config[col].renderer = fn;
8721     },
8722
8723     /**
8724      * Returns the width for the specified column.
8725      * @param {Number} col The column index
8726      * @param (optional) {String} gridSize bootstrap width size.
8727      * @return {Number}
8728      */
8729     getColumnWidth : function(col, gridSize)
8730         {
8731                 var cfg = this.config[col];
8732                 
8733                 if (typeof(gridSize) == 'undefined') {
8734                         return cfg.width * 1 || this.defaultWidth;
8735                 }
8736                 if (gridSize === false) { // if we set it..
8737                         return cfg.width || false;
8738                 }
8739                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8740                 
8741                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8742                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8743                                 continue;
8744                         }
8745                         return cfg[ sizes[i] ];
8746                 }
8747                 return 1;
8748                 
8749     },
8750
8751     /**
8752      * Sets the width for a column.
8753      * @param {Number} col The column index
8754      * @param {Number} width The new width
8755      */
8756     setColumnWidth : function(col, width, suppressEvent){
8757         this.config[col].width = width;
8758         this.totalWidth = null;
8759         if(!suppressEvent){
8760              this.fireEvent("widthchange", this, col, width);
8761         }
8762     },
8763
8764     /**
8765      * Returns the total width of all columns.
8766      * @param {Boolean} includeHidden True to include hidden column widths
8767      * @return {Number}
8768      */
8769     getTotalWidth : function(includeHidden){
8770         if(!this.totalWidth){
8771             this.totalWidth = 0;
8772             for(var i = 0, len = this.config.length; i < len; i++){
8773                 if(includeHidden || !this.isHidden(i)){
8774                     this.totalWidth += this.getColumnWidth(i);
8775                 }
8776             }
8777         }
8778         return this.totalWidth;
8779     },
8780
8781     /**
8782      * Returns the header for the specified column.
8783      * @param {Number} col The column index
8784      * @return {String}
8785      */
8786     getColumnHeader : function(col){
8787         return this.config[col].header;
8788     },
8789
8790     /**
8791      * Sets the header for a column.
8792      * @param {Number} col The column index
8793      * @param {String} header The new header
8794      */
8795     setColumnHeader : function(col, header){
8796         this.config[col].header = header;
8797         this.fireEvent("headerchange", this, col, header);
8798     },
8799
8800     /**
8801      * Returns the tooltip for the specified column.
8802      * @param {Number} col The column index
8803      * @return {String}
8804      */
8805     getColumnTooltip : function(col){
8806             return this.config[col].tooltip;
8807     },
8808     /**
8809      * Sets the tooltip for a column.
8810      * @param {Number} col The column index
8811      * @param {String} tooltip The new tooltip
8812      */
8813     setColumnTooltip : function(col, tooltip){
8814             this.config[col].tooltip = tooltip;
8815     },
8816
8817     /**
8818      * Returns the dataIndex for the specified column.
8819      * @param {Number} col The column index
8820      * @return {Number}
8821      */
8822     getDataIndex : function(col){
8823         return this.config[col].dataIndex;
8824     },
8825
8826     /**
8827      * Sets the dataIndex for a column.
8828      * @param {Number} col The column index
8829      * @param {Number} dataIndex The new dataIndex
8830      */
8831     setDataIndex : function(col, dataIndex){
8832         this.config[col].dataIndex = dataIndex;
8833     },
8834
8835     
8836     
8837     /**
8838      * Returns true if the cell is editable.
8839      * @param {Number} colIndex The column index
8840      * @param {Number} rowIndex The row index - this is nto actually used..?
8841      * @return {Boolean}
8842      */
8843     isCellEditable : function(colIndex, rowIndex){
8844         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8845     },
8846
8847     /**
8848      * Returns the editor defined for the cell/column.
8849      * return false or null to disable editing.
8850      * @param {Number} colIndex The column index
8851      * @param {Number} rowIndex The row index
8852      * @return {Object}
8853      */
8854     getCellEditor : function(colIndex, rowIndex){
8855         return this.config[colIndex].editor;
8856     },
8857
8858     /**
8859      * Sets if a column is editable.
8860      * @param {Number} col The column index
8861      * @param {Boolean} editable True if the column is editable
8862      */
8863     setEditable : function(col, editable){
8864         this.config[col].editable = editable;
8865     },
8866
8867
8868     /**
8869      * Returns true if the column is hidden.
8870      * @param {Number} colIndex The column index
8871      * @return {Boolean}
8872      */
8873     isHidden : function(colIndex){
8874         return this.config[colIndex].hidden;
8875     },
8876
8877
8878     /**
8879      * Returns true if the column width cannot be changed
8880      */
8881     isFixed : function(colIndex){
8882         return this.config[colIndex].fixed;
8883     },
8884
8885     /**
8886      * Returns true if the column can be resized
8887      * @return {Boolean}
8888      */
8889     isResizable : function(colIndex){
8890         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8891     },
8892     /**
8893      * Sets if a column is hidden.
8894      * @param {Number} colIndex The column index
8895      * @param {Boolean} hidden True if the column is hidden
8896      */
8897     setHidden : function(colIndex, hidden){
8898         this.config[colIndex].hidden = hidden;
8899         this.totalWidth = null;
8900         this.fireEvent("hiddenchange", this, colIndex, hidden);
8901     },
8902
8903     /**
8904      * Sets the editor for a column.
8905      * @param {Number} col The column index
8906      * @param {Object} editor The editor object
8907      */
8908     setEditor : function(col, editor){
8909         this.config[col].editor = editor;
8910     },
8911     /**
8912      * Add a column (experimental...) - defaults to adding to the end..
8913      * @param {Object} config 
8914     */
8915     addColumn : function(c)
8916     {
8917     
8918         var i = this.config.length;
8919         this.config[i] = c;
8920         
8921         if(typeof c.dataIndex == "undefined"){
8922             c.dataIndex = i;
8923         }
8924         if(typeof c.renderer == "string"){
8925             c.renderer = Roo.util.Format[c.renderer];
8926         }
8927         if(typeof c.id == "undefined"){
8928             c.id = Roo.id();
8929         }
8930         if(c.editor && c.editor.xtype){
8931             c.editor  = Roo.factory(c.editor, Roo.grid);
8932         }
8933         if(c.editor && c.editor.isFormField){
8934             c.editor = new Roo.grid.GridEditor(c.editor);
8935         }
8936         this.lookup[c.id] = c;
8937     }
8938     
8939 });
8940
8941 Roo.grid.ColumnModel.defaultRenderer = function(value)
8942 {
8943     if(typeof value == "object") {
8944         return value;
8945     }
8946         if(typeof value == "string" && value.length < 1){
8947             return "&#160;";
8948         }
8949     
8950         return String.format("{0}", value);
8951 };
8952
8953 // Alias for backwards compatibility
8954 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8955 /*
8956  * Based on:
8957  * Ext JS Library 1.1.1
8958  * Copyright(c) 2006-2007, Ext JS, LLC.
8959  *
8960  * Originally Released Under LGPL - original licence link has changed is not relivant.
8961  *
8962  * Fork - LGPL
8963  * <script type="text/javascript">
8964  */
8965  
8966 /**
8967  * @class Roo.LoadMask
8968  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8969  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8970  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8971  * element's UpdateManager load indicator and will be destroyed after the initial load.
8972  * @constructor
8973  * Create a new LoadMask
8974  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8975  * @param {Object} config The config object
8976  */
8977 Roo.LoadMask = function(el, config){
8978     this.el = Roo.get(el);
8979     Roo.apply(this, config);
8980     if(this.store){
8981         this.store.on('beforeload', this.onBeforeLoad, this);
8982         this.store.on('load', this.onLoad, this);
8983         this.store.on('loadexception', this.onLoadException, this);
8984         this.removeMask = false;
8985     }else{
8986         var um = this.el.getUpdateManager();
8987         um.showLoadIndicator = false; // disable the default indicator
8988         um.on('beforeupdate', this.onBeforeLoad, this);
8989         um.on('update', this.onLoad, this);
8990         um.on('failure', this.onLoad, this);
8991         this.removeMask = true;
8992     }
8993 };
8994
8995 Roo.LoadMask.prototype = {
8996     /**
8997      * @cfg {Boolean} removeMask
8998      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8999      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9000      */
9001     removeMask : false,
9002     /**
9003      * @cfg {String} msg
9004      * The text to display in a centered loading message box (defaults to 'Loading...')
9005      */
9006     msg : 'Loading...',
9007     /**
9008      * @cfg {String} msgCls
9009      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9010      */
9011     msgCls : 'x-mask-loading',
9012
9013     /**
9014      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9015      * @type Boolean
9016      */
9017     disabled: false,
9018
9019     /**
9020      * Disables the mask to prevent it from being displayed
9021      */
9022     disable : function(){
9023        this.disabled = true;
9024     },
9025
9026     /**
9027      * Enables the mask so that it can be displayed
9028      */
9029     enable : function(){
9030         this.disabled = false;
9031     },
9032     
9033     onLoadException : function()
9034     {
9035         Roo.log(arguments);
9036         
9037         if (typeof(arguments[3]) != 'undefined') {
9038             Roo.MessageBox.alert("Error loading",arguments[3]);
9039         } 
9040         /*
9041         try {
9042             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9043                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9044             }   
9045         } catch(e) {
9046             
9047         }
9048         */
9049     
9050         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9051     },
9052     // private
9053     onLoad : function()
9054     {
9055         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9056     },
9057
9058     // private
9059     onBeforeLoad : function(){
9060         if(!this.disabled){
9061             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9062         }
9063     },
9064
9065     // private
9066     destroy : function(){
9067         if(this.store){
9068             this.store.un('beforeload', this.onBeforeLoad, this);
9069             this.store.un('load', this.onLoad, this);
9070             this.store.un('loadexception', this.onLoadException, this);
9071         }else{
9072             var um = this.el.getUpdateManager();
9073             um.un('beforeupdate', this.onBeforeLoad, this);
9074             um.un('update', this.onLoad, this);
9075             um.un('failure', this.onLoad, this);
9076         }
9077     }
9078 };/**
9079  * @class Roo.bootstrap.Table
9080  * @licence LGBL
9081  * @extends Roo.bootstrap.Component
9082  * @children Roo.bootstrap.TableBody
9083  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9084  * Similar to Roo.grid.Grid
9085  * <pre><code>
9086  var table = Roo.factory({
9087     xtype : 'Table',
9088     xns : Roo.bootstrap,
9089     autoSizeColumns: true,
9090     
9091     
9092     store : {
9093         xtype : 'Store',
9094         xns : Roo.data,
9095         remoteSort : true,
9096         sortInfo : { direction : 'ASC', field: 'name' },
9097         proxy : {
9098            xtype : 'HttpProxy',
9099            xns : Roo.data,
9100            method : 'GET',
9101            url : 'https://example.com/some.data.url.json'
9102         },
9103         reader : {
9104            xtype : 'JsonReader',
9105            xns : Roo.data,
9106            fields : [ 'id', 'name', whatever' ],
9107            id : 'id',
9108            root : 'data'
9109         }
9110     },
9111     cm : [
9112         {
9113             xtype : 'ColumnModel',
9114             xns : Roo.grid,
9115             align : 'center',
9116             cursor : 'pointer',
9117             dataIndex : 'is_in_group',
9118             header : "Name",
9119             sortable : true,
9120             renderer : function(v, x , r) {  
9121             
9122                 return String.format("{0}", v)
9123             }
9124             width : 3
9125         } // more columns..
9126     ],
9127     selModel : {
9128         xtype : 'RowSelectionModel',
9129         xns : Roo.bootstrap.Table
9130         // you can add listeners to catch selection change here....
9131     }
9132      
9133
9134  });
9135  // set any options
9136  grid.render(Roo.get("some-div"));
9137 </code></pre>
9138
9139 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9140
9141
9142
9143  *
9144  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9145  * @cfg {Roo.data.Store} store The data store to use
9146  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9147  * 
9148  * @cfg {String} cls table class
9149  *
9150  *
9151  * @cfg {string} empty_results  Text to display for no results 
9152  * @cfg {boolean} striped Should the rows be alternative striped
9153  * @cfg {boolean} bordered Add borders to the table
9154  * @cfg {boolean} hover Add hover highlighting
9155  * @cfg {boolean} condensed Format condensed
9156  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9157  *                also adds table-responsive (see bootstrap docs for details)
9158  * @cfg {Boolean} loadMask (true|false) default false
9159  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9160  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9161  * @cfg {Boolean} rowSelection (true|false) default false
9162  * @cfg {Boolean} cellSelection (true|false) default false
9163  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9164  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9165  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9166  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9167  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9168  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9169  *
9170  * 
9171  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9172  * 
9173  * @constructor
9174  * Create a new Table
9175  * @param {Object} config The config object
9176  */
9177
9178 Roo.bootstrap.Table = function(config)
9179 {
9180     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9181      
9182     // BC...
9183     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9184     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9185     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9186     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9187     
9188     this.view = this; // compat with grid.
9189     
9190     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9191     if (this.sm) {
9192         this.sm.grid = this;
9193         this.selModel = Roo.factory(this.sm, Roo.grid);
9194         this.sm = this.selModel;
9195         this.sm.xmodule = this.xmodule || false;
9196     }
9197     
9198     if (this.cm && typeof(this.cm.config) == 'undefined') {
9199         this.colModel = new Roo.grid.ColumnModel(this.cm);
9200         this.cm = this.colModel;
9201         this.cm.xmodule = this.xmodule || false;
9202     }
9203     if (this.store) {
9204         this.store= Roo.factory(this.store, Roo.data);
9205         this.ds = this.store;
9206         this.ds.xmodule = this.xmodule || false;
9207          
9208     }
9209     if (this.footer && this.store) {
9210         this.footer.dataSource = this.ds;
9211         this.footer = Roo.factory(this.footer);
9212     }
9213     
9214     /** @private */
9215     this.addEvents({
9216         /**
9217          * @event cellclick
9218          * Fires when a cell is clicked
9219          * @param {Roo.bootstrap.Table} this
9220          * @param {Roo.Element} el
9221          * @param {Number} rowIndex
9222          * @param {Number} columnIndex
9223          * @param {Roo.EventObject} e
9224          */
9225         "cellclick" : true,
9226         /**
9227          * @event celldblclick
9228          * Fires when a cell is double clicked
9229          * @param {Roo.bootstrap.Table} this
9230          * @param {Roo.Element} el
9231          * @param {Number} rowIndex
9232          * @param {Number} columnIndex
9233          * @param {Roo.EventObject} e
9234          */
9235         "celldblclick" : true,
9236         /**
9237          * @event rowclick
9238          * Fires when a row is clicked
9239          * @param {Roo.bootstrap.Table} this
9240          * @param {Roo.Element} el
9241          * @param {Number} rowIndex
9242          * @param {Roo.EventObject} e
9243          */
9244         "rowclick" : true,
9245         /**
9246          * @event rowdblclick
9247          * Fires when a row is double clicked
9248          * @param {Roo.bootstrap.Table} this
9249          * @param {Roo.Element} el
9250          * @param {Number} rowIndex
9251          * @param {Roo.EventObject} e
9252          */
9253         "rowdblclick" : true,
9254         /**
9255          * @event mouseover
9256          * Fires when a mouseover occur
9257          * @param {Roo.bootstrap.Table} this
9258          * @param {Roo.Element} el
9259          * @param {Number} rowIndex
9260          * @param {Number} columnIndex
9261          * @param {Roo.EventObject} e
9262          */
9263         "mouseover" : true,
9264         /**
9265          * @event mouseout
9266          * Fires when a mouseout occur
9267          * @param {Roo.bootstrap.Table} this
9268          * @param {Roo.Element} el
9269          * @param {Number} rowIndex
9270          * @param {Number} columnIndex
9271          * @param {Roo.EventObject} e
9272          */
9273         "mouseout" : true,
9274         /**
9275          * @event rowclass
9276          * Fires when a row is rendered, so you can change add a style to it.
9277          * @param {Roo.bootstrap.Table} this
9278          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9279          */
9280         'rowclass' : true,
9281           /**
9282          * @event rowsrendered
9283          * Fires when all the  rows have been rendered
9284          * @param {Roo.bootstrap.Table} this
9285          */
9286         'rowsrendered' : true,
9287         /**
9288          * @event contextmenu
9289          * The raw contextmenu event for the entire grid.
9290          * @param {Roo.EventObject} e
9291          */
9292         "contextmenu" : true,
9293         /**
9294          * @event rowcontextmenu
9295          * Fires when a row is right clicked
9296          * @param {Roo.bootstrap.Table} this
9297          * @param {Number} rowIndex
9298          * @param {Roo.EventObject} e
9299          */
9300         "rowcontextmenu" : true,
9301         /**
9302          * @event cellcontextmenu
9303          * Fires when a cell is right clicked
9304          * @param {Roo.bootstrap.Table} this
9305          * @param {Number} rowIndex
9306          * @param {Number} cellIndex
9307          * @param {Roo.EventObject} e
9308          */
9309          "cellcontextmenu" : true,
9310          /**
9311          * @event headercontextmenu
9312          * Fires when a header is right clicked
9313          * @param {Roo.bootstrap.Table} this
9314          * @param {Number} columnIndex
9315          * @param {Roo.EventObject} e
9316          */
9317         "headercontextmenu" : true,
9318         /**
9319          * @event mousedown
9320          * The raw mousedown event for the entire grid.
9321          * @param {Roo.EventObject} e
9322          */
9323         "mousedown" : true
9324         
9325     });
9326 };
9327
9328 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9329     
9330     cls: false,
9331     
9332     empty_results : '',
9333     striped : false,
9334     scrollBody : false,
9335     bordered: false,
9336     hover:  false,
9337     condensed : false,
9338     responsive : false,
9339     sm : false,
9340     cm : false,
9341     store : false,
9342     loadMask : false,
9343     footerShow : true,
9344     headerShow : true,
9345     enableColumnResize: true,
9346     disableAutoSize: false,
9347   
9348     rowSelection : false,
9349     cellSelection : false,
9350     layout : false,
9351
9352     minColumnWidth : 50,
9353     
9354     // Roo.Element - the tbody
9355     bodyEl: false,  // <tbody> Roo.Element - thead element    
9356     headEl: false,  // <thead> Roo.Element - thead element
9357     resizeProxy : false, // proxy element for dragging?
9358
9359
9360     
9361     container: false, // used by gridpanel...
9362     
9363     lazyLoad : false,
9364     
9365     CSS : Roo.util.CSS,
9366     
9367     auto_hide_footer : false,
9368     
9369     view: false, // actually points to this..
9370     
9371     getAutoCreate : function()
9372     {
9373         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9374         
9375         cfg = {
9376             tag: 'table',
9377             cls : 'table', 
9378             cn : []
9379         };
9380         // this get's auto added by panel.Grid
9381         if (this.scrollBody) {
9382             cfg.cls += ' table-body-fixed';
9383         }    
9384         if (this.striped) {
9385             cfg.cls += ' table-striped';
9386         }
9387         
9388         if (this.hover) {
9389             cfg.cls += ' table-hover';
9390         }
9391         if (this.bordered) {
9392             cfg.cls += ' table-bordered';
9393         }
9394         if (this.condensed) {
9395             cfg.cls += ' table-condensed';
9396         }
9397         
9398         if (this.responsive) {
9399             cfg.cls += ' table-responsive';
9400         }
9401         
9402         if (this.cls) {
9403             cfg.cls+=  ' ' +this.cls;
9404         }
9405         
9406         
9407         
9408         if (this.layout) {
9409             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9410         }
9411         
9412         if(this.store || this.cm){
9413             if(this.headerShow){
9414                 cfg.cn.push(this.renderHeader());
9415             }
9416             
9417             cfg.cn.push(this.renderBody());
9418             
9419             if(this.footerShow){
9420                 cfg.cn.push(this.renderFooter());
9421             }
9422             // where does this come from?
9423             //cfg.cls+=  ' TableGrid';
9424         }
9425         
9426         return { cn : [ cfg ] };
9427     },
9428     
9429     initEvents : function()
9430     {   
9431         if(!this.store || !this.cm){
9432             return;
9433         }
9434         if (this.selModel) {
9435             this.selModel.initEvents();
9436         }
9437         
9438         
9439         //Roo.log('initEvents with ds!!!!');
9440         
9441         this.bodyEl = this.el.select('tbody', true).first();
9442         this.headEl = this.el.select('thead', true).first();
9443         this.mainFoot = this.el.select('tfoot', true).first();
9444         
9445         
9446         
9447         
9448         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9449             e.on('click', this.sort, this);
9450         }, this);
9451         
9452         
9453         // why is this done????? = it breaks dialogs??
9454         //this.parent().el.setStyle('position', 'relative');
9455         
9456         
9457         if (this.footer) {
9458             this.footer.parentId = this.id;
9459             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9460             
9461             if(this.lazyLoad){
9462                 this.el.select('tfoot tr td').first().addClass('hide');
9463             }
9464         } 
9465         
9466         if(this.loadMask) {
9467             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9468         }
9469         
9470         this.store.on('load', this.onLoad, this);
9471         this.store.on('beforeload', this.onBeforeLoad, this);
9472         this.store.on('update', this.onUpdate, this);
9473         this.store.on('add', this.onAdd, this);
9474         this.store.on("clear", this.clear, this);
9475         
9476         this.el.on("contextmenu", this.onContextMenu, this);
9477         
9478         
9479         this.cm.on("headerchange", this.onHeaderChange, this);
9480         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9481
9482  //?? does bodyEl get replaced on render?
9483         this.bodyEl.on("click", this.onClick, this);
9484         this.bodyEl.on("dblclick", this.onDblClick, this);        
9485         this.bodyEl.on('scroll', this.onBodyScroll, this);
9486
9487         // guessing mainbody will work - this relays usually caught by selmodel at present.
9488         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9489   
9490   
9491         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9492         
9493   
9494         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9495             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9496         }
9497         
9498         this.initCSS();
9499     },
9500     // Compatibility with grid - we implement all the view features at present.
9501     getView : function()
9502     {
9503         return this;
9504     },
9505     
9506     initCSS : function()
9507     {
9508         if(this.disableAutoSize) {
9509             return;
9510         }
9511         
9512         var cm = this.cm, styles = [];
9513         this.CSS.removeStyleSheet(this.id + '-cssrules');
9514         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9515         // we can honour xs/sm/md/xl  as widths...
9516         // we first have to decide what widht we are currently at...
9517         var sz = Roo.getGridSize();
9518         
9519         var total = 0;
9520         var last = -1;
9521         var cols = []; // visable cols.
9522         var total_abs = 0;
9523         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9524             var w = cm.getColumnWidth(i, false);
9525             if(cm.isHidden(i)){
9526                 cols.push( { rel : false, abs : 0 });
9527                 continue;
9528             }
9529             if (w !== false) {
9530                 cols.push( { rel : false, abs : w });
9531                 total_abs += w;
9532                 last = i; // not really..
9533                 continue;
9534             }
9535             var w = cm.getColumnWidth(i, sz);
9536             if (w > 0) {
9537                 last = i
9538             }
9539             total += w;
9540             cols.push( { rel : w, abs : false });
9541         }
9542         
9543         var avail = this.bodyEl.dom.clientWidth - total_abs;
9544         
9545         var unitWidth = Math.floor(avail / total);
9546         var rem = avail - (unitWidth * total);
9547         
9548         var hidden, width, pos = 0 , splithide , left;
9549         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9550             
9551             hidden = 'display:none;';
9552             left = '';
9553             width  = 'width:0px;';
9554             splithide = '';
9555             if(!cm.isHidden(i)){
9556                 hidden = '';
9557                 
9558                 
9559                 // we can honour xs/sm/md/xl ?
9560                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9561                 if (w===0) {
9562                     hidden = 'display:none;';
9563                 }
9564                 // width should return a small number...
9565                 if (i == last) {
9566                     w+=rem; // add the remaining with..
9567                 }
9568                 pos += w;
9569                 left = "left:" + (pos -4) + "px;";
9570                 width = "width:" + w+ "px;";
9571                 
9572             }
9573             if (this.responsive) {
9574                 width = '';
9575                 left = '';
9576                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9577                 splithide = 'display: none;';
9578             }
9579             
9580             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9581             if (this.headEl) {
9582                 if (i == last) {
9583                     splithide = 'display:none;';
9584                 }
9585                 
9586                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9587                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9588                             // this is the popover version..
9589                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9590                 );
9591             }
9592             
9593         }
9594         //Roo.log(styles.join(''));
9595         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9596         
9597     },
9598     
9599     
9600     
9601     onContextMenu : function(e, t)
9602     {
9603         this.processEvent("contextmenu", e);
9604     },
9605     
9606     processEvent : function(name, e)
9607     {
9608         if (name != 'touchstart' ) {
9609             this.fireEvent(name, e);    
9610         }
9611         
9612         var t = e.getTarget();
9613         
9614         var cell = Roo.get(t);
9615         
9616         if(!cell){
9617             return;
9618         }
9619         
9620         if(cell.findParent('tfoot', false, true)){
9621             return;
9622         }
9623         
9624         if(cell.findParent('thead', false, true)){
9625             
9626             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9627                 cell = Roo.get(t).findParent('th', false, true);
9628                 if (!cell) {
9629                     Roo.log("failed to find th in thead?");
9630                     Roo.log(e.getTarget());
9631                     return;
9632                 }
9633             }
9634             
9635             var cellIndex = cell.dom.cellIndex;
9636             
9637             var ename = name == 'touchstart' ? 'click' : name;
9638             this.fireEvent("header" + ename, this, cellIndex, e);
9639             
9640             return;
9641         }
9642         
9643         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9644             cell = Roo.get(t).findParent('td', false, true);
9645             if (!cell) {
9646                 Roo.log("failed to find th in tbody?");
9647                 Roo.log(e.getTarget());
9648                 return;
9649             }
9650         }
9651         
9652         var row = cell.findParent('tr', false, true);
9653         var cellIndex = cell.dom.cellIndex;
9654         var rowIndex = row.dom.rowIndex - 1;
9655         
9656         if(row !== false){
9657             
9658             this.fireEvent("row" + name, this, rowIndex, e);
9659             
9660             if(cell !== false){
9661             
9662                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9663             }
9664         }
9665         
9666     },
9667     
9668     onMouseover : function(e, el)
9669     {
9670         var cell = Roo.get(el);
9671         
9672         if(!cell){
9673             return;
9674         }
9675         
9676         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9677             cell = cell.findParent('td', false, true);
9678         }
9679         
9680         var row = cell.findParent('tr', false, true);
9681         var cellIndex = cell.dom.cellIndex;
9682         var rowIndex = row.dom.rowIndex - 1; // start from 0
9683         
9684         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9685         
9686     },
9687     
9688     onMouseout : function(e, el)
9689     {
9690         var cell = Roo.get(el);
9691         
9692         if(!cell){
9693             return;
9694         }
9695         
9696         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9697             cell = cell.findParent('td', false, true);
9698         }
9699         
9700         var row = cell.findParent('tr', false, true);
9701         var cellIndex = cell.dom.cellIndex;
9702         var rowIndex = row.dom.rowIndex - 1; // start from 0
9703         
9704         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9705         
9706     },
9707     
9708     onClick : function(e, el)
9709     {
9710         var cell = Roo.get(el);
9711         
9712         if(!cell || (!this.cellSelection && !this.rowSelection)){
9713             return;
9714         }
9715         
9716         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9717             cell = cell.findParent('td', false, true);
9718         }
9719         
9720         if(!cell || typeof(cell) == 'undefined'){
9721             return;
9722         }
9723         
9724         var row = cell.findParent('tr', false, true);
9725         
9726         if(!row || typeof(row) == 'undefined'){
9727             return;
9728         }
9729         
9730         var cellIndex = cell.dom.cellIndex;
9731         var rowIndex = this.getRowIndex(row);
9732         
9733         // why??? - should these not be based on SelectionModel?
9734         //if(this.cellSelection){
9735             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9736         //}
9737         
9738         //if(this.rowSelection){
9739             this.fireEvent('rowclick', this, row, rowIndex, e);
9740         //}
9741          
9742     },
9743         
9744     onDblClick : function(e,el)
9745     {
9746         var cell = Roo.get(el);
9747         
9748         if(!cell || (!this.cellSelection && !this.rowSelection)){
9749             return;
9750         }
9751         
9752         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9753             cell = cell.findParent('td', false, true);
9754         }
9755         
9756         if(!cell || typeof(cell) == 'undefined'){
9757             return;
9758         }
9759         
9760         var row = cell.findParent('tr', false, true);
9761         
9762         if(!row || typeof(row) == 'undefined'){
9763             return;
9764         }
9765         
9766         var cellIndex = cell.dom.cellIndex;
9767         var rowIndex = this.getRowIndex(row);
9768         
9769         if(this.cellSelection){
9770             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9771         }
9772         
9773         if(this.rowSelection){
9774             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9775         }
9776     },
9777     findRowIndex : function(el)
9778     {
9779         var cell = Roo.get(el);
9780         if(!cell) {
9781             return false;
9782         }
9783         var row = cell.findParent('tr', false, true);
9784         
9785         if(!row || typeof(row) == 'undefined'){
9786             return false;
9787         }
9788         return this.getRowIndex(row);
9789     },
9790     sort : function(e,el)
9791     {
9792         var col = Roo.get(el);
9793         
9794         if(!col.hasClass('sortable')){
9795             return;
9796         }
9797         
9798         var sort = col.attr('sort');
9799         var dir = 'ASC';
9800         
9801         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9802             dir = 'DESC';
9803         }
9804         
9805         this.store.sortInfo = {field : sort, direction : dir};
9806         
9807         if (this.footer) {
9808             Roo.log("calling footer first");
9809             this.footer.onClick('first');
9810         } else {
9811         
9812             this.store.load({ params : { start : 0 } });
9813         }
9814     },
9815     
9816     renderHeader : function()
9817     {
9818         var header = {
9819             tag: 'thead',
9820             cn : []
9821         };
9822         
9823         var cm = this.cm;
9824         this.totalWidth = 0;
9825         
9826         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9827             
9828             var config = cm.config[i];
9829             
9830             var c = {
9831                 tag: 'th',
9832                 cls : 'x-hcol-' + i,
9833                 style : '',
9834                 
9835                 html: cm.getColumnHeader(i)
9836             };
9837             
9838             var tooltip = cm.getColumnTooltip(i);
9839             if (tooltip) {
9840                 c.tooltip = tooltip;
9841             }
9842             
9843             
9844             var hh = '';
9845             
9846             if(typeof(config.sortable) != 'undefined' && config.sortable){
9847                 c.cls += ' sortable';
9848                 c.html = '<i class="fa"></i>' + c.html;
9849             }
9850             
9851             // could use BS4 hidden-..-down 
9852             
9853             if(typeof(config.lgHeader) != 'undefined'){
9854                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9855             }
9856             
9857             if(typeof(config.mdHeader) != 'undefined'){
9858                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9859             }
9860             
9861             if(typeof(config.smHeader) != 'undefined'){
9862                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9863             }
9864             
9865             if(typeof(config.xsHeader) != 'undefined'){
9866                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9867             }
9868             
9869             if(hh.length){
9870                 c.html = hh;
9871             }
9872             
9873             if(typeof(config.tooltip) != 'undefined'){
9874                 c.tooltip = config.tooltip;
9875             }
9876             
9877             if(typeof(config.colspan) != 'undefined'){
9878                 c.colspan = config.colspan;
9879             }
9880             
9881             // hidden is handled by CSS now
9882             
9883             if(typeof(config.dataIndex) != 'undefined'){
9884                 c.sort = config.dataIndex;
9885             }
9886             
9887            
9888             
9889             if(typeof(config.align) != 'undefined' && config.align.length){
9890                 c.style += ' text-align:' + config.align + ';';
9891             }
9892             
9893             /* width is done in CSS
9894              *if(typeof(config.width) != 'undefined'){
9895                 c.style += ' width:' + config.width + 'px;';
9896                 this.totalWidth += config.width;
9897             } else {
9898                 this.totalWidth += 100; // assume minimum of 100 per column?
9899             }
9900             */
9901             
9902             if(typeof(config.cls) != 'undefined'){
9903                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9904             }
9905             // this is the bit that doesnt reall work at all...
9906             
9907             if (this.responsive) {
9908                  
9909             
9910                 ['xs','sm','md','lg'].map(function(size){
9911                     
9912                     if(typeof(config[size]) == 'undefined'){
9913                         return;
9914                     }
9915                      
9916                     if (!config[size]) { // 0 = hidden
9917                         // BS 4 '0' is treated as hide that column and below.
9918                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9919                         return;
9920                     }
9921                     
9922                     c.cls += ' col-' + size + '-' + config[size] + (
9923                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9924                     );
9925                     
9926                     
9927                 });
9928             }
9929             // at the end?
9930             
9931             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9932             
9933             
9934             
9935             
9936             header.cn.push(c)
9937         }
9938         
9939         return header;
9940     },
9941     
9942     renderBody : function()
9943     {
9944         var body = {
9945             tag: 'tbody',
9946             cn : [
9947                 {
9948                     tag: 'tr',
9949                     cn : [
9950                         {
9951                             tag : 'td',
9952                             colspan :  this.cm.getColumnCount()
9953                         }
9954                     ]
9955                 }
9956             ]
9957         };
9958         
9959         return body;
9960     },
9961     
9962     renderFooter : function()
9963     {
9964         var footer = {
9965             tag: 'tfoot',
9966             cn : [
9967                 {
9968                     tag: 'tr',
9969                     cn : [
9970                         {
9971                             tag : 'td',
9972                             colspan :  this.cm.getColumnCount()
9973                         }
9974                     ]
9975                 }
9976             ]
9977         };
9978         
9979         return footer;
9980     },
9981     
9982     
9983     
9984     onLoad : function()
9985     {
9986 //        Roo.log('ds onload');
9987         this.clear();
9988         
9989         var _this = this;
9990         var cm = this.cm;
9991         var ds = this.store;
9992         
9993         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9994             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9995             if (_this.store.sortInfo) {
9996                     
9997                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9998                     e.select('i', true).addClass(['fa-arrow-up']);
9999                 }
10000                 
10001                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10002                     e.select('i', true).addClass(['fa-arrow-down']);
10003                 }
10004             }
10005         });
10006         
10007         var tbody =  this.bodyEl;
10008               
10009         if(ds.getCount() > 0){
10010             ds.data.each(function(d,rowIndex){
10011                 var row =  this.renderRow(cm, ds, rowIndex);
10012                 
10013                 tbody.createChild(row);
10014                 
10015                 var _this = this;
10016                 
10017                 if(row.cellObjects.length){
10018                     Roo.each(row.cellObjects, function(r){
10019                         _this.renderCellObject(r);
10020                     })
10021                 }
10022                 
10023             }, this);
10024         } else if (this.empty_results.length) {
10025             this.el.mask(this.empty_results, 'no-spinner');
10026         }
10027         
10028         var tfoot = this.el.select('tfoot', true).first();
10029         
10030         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10031             
10032             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10033             
10034             var total = this.ds.getTotalCount();
10035             
10036             if(this.footer.pageSize < total){
10037                 this.mainFoot.show();
10038             }
10039         }
10040         
10041         Roo.each(this.el.select('tbody td', true).elements, function(e){
10042             e.on('mouseover', _this.onMouseover, _this);
10043         });
10044         
10045         Roo.each(this.el.select('tbody td', true).elements, function(e){
10046             e.on('mouseout', _this.onMouseout, _this);
10047         });
10048         this.fireEvent('rowsrendered', this);
10049         
10050         this.autoSize();
10051         
10052         this.initCSS(); /// resize cols
10053
10054         
10055     },
10056     
10057     
10058     onUpdate : function(ds,record)
10059     {
10060         this.refreshRow(record);
10061         this.autoSize();
10062     },
10063     
10064     onRemove : function(ds, record, index, isUpdate){
10065         if(isUpdate !== true){
10066             this.fireEvent("beforerowremoved", this, index, record);
10067         }
10068         var bt = this.bodyEl.dom;
10069         
10070         var rows = this.el.select('tbody > tr', true).elements;
10071         
10072         if(typeof(rows[index]) != 'undefined'){
10073             bt.removeChild(rows[index].dom);
10074         }
10075         
10076 //        if(bt.rows[index]){
10077 //            bt.removeChild(bt.rows[index]);
10078 //        }
10079         
10080         if(isUpdate !== true){
10081             //this.stripeRows(index);
10082             //this.syncRowHeights(index, index);
10083             //this.layout();
10084             this.fireEvent("rowremoved", this, index, record);
10085         }
10086     },
10087     
10088     onAdd : function(ds, records, rowIndex)
10089     {
10090         //Roo.log('on Add called');
10091         // - note this does not handle multiple adding very well..
10092         var bt = this.bodyEl.dom;
10093         for (var i =0 ; i < records.length;i++) {
10094             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10095             //Roo.log(records[i]);
10096             //Roo.log(this.store.getAt(rowIndex+i));
10097             this.insertRow(this.store, rowIndex + i, false);
10098             return;
10099         }
10100         
10101     },
10102     
10103     
10104     refreshRow : function(record){
10105         var ds = this.store, index;
10106         if(typeof record == 'number'){
10107             index = record;
10108             record = ds.getAt(index);
10109         }else{
10110             index = ds.indexOf(record);
10111             if (index < 0) {
10112                 return; // should not happen - but seems to 
10113             }
10114         }
10115         this.insertRow(ds, index, true);
10116         this.autoSize();
10117         this.onRemove(ds, record, index+1, true);
10118         this.autoSize();
10119         //this.syncRowHeights(index, index);
10120         //this.layout();
10121         this.fireEvent("rowupdated", this, index, record);
10122     },
10123     // private - called by RowSelection
10124     onRowSelect : function(rowIndex){
10125         var row = this.getRowDom(rowIndex);
10126         row.addClass(['bg-info','info']);
10127     },
10128     // private - called by RowSelection
10129     onRowDeselect : function(rowIndex)
10130     {
10131         if (rowIndex < 0) {
10132             return;
10133         }
10134         var row = this.getRowDom(rowIndex);
10135         row.removeClass(['bg-info','info']);
10136     },
10137       /**
10138      * Focuses the specified row.
10139      * @param {Number} row The row index
10140      */
10141     focusRow : function(row)
10142     {
10143         //Roo.log('GridView.focusRow');
10144         var x = this.bodyEl.dom.scrollLeft;
10145         this.focusCell(row, 0, false);
10146         this.bodyEl.dom.scrollLeft = x;
10147
10148     },
10149      /**
10150      * Focuses the specified cell.
10151      * @param {Number} row The row index
10152      * @param {Number} col The column index
10153      * @param {Boolean} hscroll false to disable horizontal scrolling
10154      */
10155     focusCell : function(row, col, hscroll)
10156     {
10157         //Roo.log('GridView.focusCell');
10158         var el = this.ensureVisible(row, col, hscroll);
10159         // not sure what focusEL achives = it's a <a> pos relative 
10160         //this.focusEl.alignTo(el, "tl-tl");
10161         //if(Roo.isGecko){
10162         //    this.focusEl.focus();
10163         //}else{
10164         //    this.focusEl.focus.defer(1, this.focusEl);
10165         //}
10166     },
10167     
10168      /**
10169      * Scrolls the specified cell into view
10170      * @param {Number} row The row index
10171      * @param {Number} col The column index
10172      * @param {Boolean} hscroll false to disable horizontal scrolling
10173      */
10174     ensureVisible : function(row, col, hscroll)
10175     {
10176         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10177         //return null; //disable for testing.
10178         if(typeof row != "number"){
10179             row = row.rowIndex;
10180         }
10181         if(row < 0 && row >= this.ds.getCount()){
10182             return  null;
10183         }
10184         col = (col !== undefined ? col : 0);
10185         var cm = this.cm;
10186         while(cm.isHidden(col)){
10187             col++;
10188         }
10189
10190         var el = this.getCellDom(row, col);
10191         if(!el){
10192             return null;
10193         }
10194         var c = this.bodyEl.dom;
10195
10196         var ctop = parseInt(el.offsetTop, 10);
10197         var cleft = parseInt(el.offsetLeft, 10);
10198         var cbot = ctop + el.offsetHeight;
10199         var cright = cleft + el.offsetWidth;
10200
10201         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10202         var ch = 0; //?? header is not withing the area?
10203         var stop = parseInt(c.scrollTop, 10);
10204         var sleft = parseInt(c.scrollLeft, 10);
10205         var sbot = stop + ch;
10206         var sright = sleft + c.clientWidth;
10207         /*
10208         Roo.log('GridView.ensureVisible:' +
10209                 ' ctop:' + ctop +
10210                 ' c.clientHeight:' + c.clientHeight +
10211                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10212                 ' stop:' + stop +
10213                 ' cbot:' + cbot +
10214                 ' sbot:' + sbot +
10215                 ' ch:' + ch  
10216                 );
10217         */
10218         if(ctop < stop){
10219             c.scrollTop = ctop;
10220             //Roo.log("set scrolltop to ctop DISABLE?");
10221         }else if(cbot > sbot){
10222             //Roo.log("set scrolltop to cbot-ch");
10223             c.scrollTop = cbot-ch;
10224         }
10225
10226         if(hscroll !== false){
10227             if(cleft < sleft){
10228                 c.scrollLeft = cleft;
10229             }else if(cright > sright){
10230                 c.scrollLeft = cright-c.clientWidth;
10231             }
10232         }
10233
10234         return el;
10235     },
10236     
10237     
10238     insertRow : function(dm, rowIndex, isUpdate){
10239         
10240         if(!isUpdate){
10241             this.fireEvent("beforerowsinserted", this, rowIndex);
10242         }
10243             //var s = this.getScrollState();
10244         var row = this.renderRow(this.cm, this.store, rowIndex);
10245         // insert before rowIndex..
10246         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10247         
10248         var _this = this;
10249                 
10250         if(row.cellObjects.length){
10251             Roo.each(row.cellObjects, function(r){
10252                 _this.renderCellObject(r);
10253             })
10254         }
10255             
10256         if(!isUpdate){
10257             this.fireEvent("rowsinserted", this, rowIndex);
10258             //this.syncRowHeights(firstRow, lastRow);
10259             //this.stripeRows(firstRow);
10260             //this.layout();
10261         }
10262         
10263     },
10264     
10265     
10266     getRowDom : function(rowIndex)
10267     {
10268         var rows = this.el.select('tbody > tr', true).elements;
10269         
10270         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10271         
10272     },
10273     getCellDom : function(rowIndex, colIndex)
10274     {
10275         var row = this.getRowDom(rowIndex);
10276         if (row === false) {
10277             return false;
10278         }
10279         var cols = row.select('td', true).elements;
10280         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10281         
10282     },
10283     
10284     // returns the object tree for a tr..
10285   
10286     
10287     renderRow : function(cm, ds, rowIndex) 
10288     {
10289         var d = ds.getAt(rowIndex);
10290         
10291         var row = {
10292             tag : 'tr',
10293             cls : 'x-row-' + rowIndex,
10294             cn : []
10295         };
10296             
10297         var cellObjects = [];
10298         
10299         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10300             var config = cm.config[i];
10301             
10302             var renderer = cm.getRenderer(i);
10303             var value = '';
10304             var id = false;
10305             
10306             if(typeof(renderer) !== 'undefined'){
10307                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10308             }
10309             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10310             // and are rendered into the cells after the row is rendered - using the id for the element.
10311             
10312             if(typeof(value) === 'object'){
10313                 id = Roo.id();
10314                 cellObjects.push({
10315                     container : id,
10316                     cfg : value 
10317                 })
10318             }
10319             
10320             var rowcfg = {
10321                 record: d,
10322                 rowIndex : rowIndex,
10323                 colIndex : i,
10324                 rowClass : ''
10325             };
10326
10327             this.fireEvent('rowclass', this, rowcfg);
10328             
10329             var td = {
10330                 tag: 'td',
10331                 // this might end up displaying HTML?
10332                 // this is too messy... - better to only do it on columsn you know are going to be too long
10333                 //tooltip : (typeof(value) === 'object') ? '' : value,
10334                 cls : rowcfg.rowClass + ' x-col-' + i,
10335                 style: '',
10336                 html: (typeof(value) === 'object') ? '' : value
10337             };
10338             
10339             if (id) {
10340                 td.id = id;
10341             }
10342             
10343             if(typeof(config.colspan) != 'undefined'){
10344                 td.colspan = config.colspan;
10345             }
10346             
10347             
10348             
10349             if(typeof(config.align) != 'undefined' && config.align.length){
10350                 td.style += ' text-align:' + config.align + ';';
10351             }
10352             if(typeof(config.valign) != 'undefined' && config.valign.length){
10353                 td.style += ' vertical-align:' + config.valign + ';';
10354             }
10355             /*
10356             if(typeof(config.width) != 'undefined'){
10357                 td.style += ' width:' +  config.width + 'px;';
10358             }
10359             */
10360             
10361             if(typeof(config.cursor) != 'undefined'){
10362                 td.style += ' cursor:' +  config.cursor + ';';
10363             }
10364             
10365             if(typeof(config.cls) != 'undefined'){
10366                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10367             }
10368             if (this.responsive) {
10369                 ['xs','sm','md','lg'].map(function(size){
10370                     
10371                     if(typeof(config[size]) == 'undefined'){
10372                         return;
10373                     }
10374                     
10375                     
10376                       
10377                     if (!config[size]) { // 0 = hidden
10378                         // BS 4 '0' is treated as hide that column and below.
10379                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10380                         return;
10381                     }
10382                     
10383                     td.cls += ' col-' + size + '-' + config[size] + (
10384                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10385                     );
10386                      
10387     
10388                 });
10389             }
10390             row.cn.push(td);
10391            
10392         }
10393         
10394         row.cellObjects = cellObjects;
10395         
10396         return row;
10397           
10398     },
10399     
10400     
10401     
10402     onBeforeLoad : function()
10403     {
10404         this.el.unmask(); // if needed.
10405     },
10406      /**
10407      * Remove all rows
10408      */
10409     clear : function()
10410     {
10411         this.el.select('tbody', true).first().dom.innerHTML = '';
10412     },
10413     /**
10414      * Show or hide a row.
10415      * @param {Number} rowIndex to show or hide
10416      * @param {Boolean} state hide
10417      */
10418     setRowVisibility : function(rowIndex, state)
10419     {
10420         var bt = this.bodyEl.dom;
10421         
10422         var rows = this.el.select('tbody > tr', true).elements;
10423         
10424         if(typeof(rows[rowIndex]) == 'undefined'){
10425             return;
10426         }
10427         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10428         
10429     },
10430     
10431     
10432     getSelectionModel : function(){
10433         if(!this.selModel){
10434             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10435         }
10436         return this.selModel;
10437     },
10438     /*
10439      * Render the Roo.bootstrap object from renderder
10440      */
10441     renderCellObject : function(r)
10442     {
10443         var _this = this;
10444         
10445         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10446         
10447         var t = r.cfg.render(r.container);
10448         
10449         if(r.cfg.cn){
10450             Roo.each(r.cfg.cn, function(c){
10451                 var child = {
10452                     container: t.getChildContainer(),
10453                     cfg: c
10454                 };
10455                 _this.renderCellObject(child);
10456             })
10457         }
10458     },
10459     /**
10460      * get the Row Index from a dom element.
10461      * @param {Roo.Element} row The row to look for
10462      * @returns {Number} the row
10463      */
10464     getRowIndex : function(row)
10465     {
10466         var rowIndex = -1;
10467         
10468         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10469             if(el != row){
10470                 return;
10471             }
10472             
10473             rowIndex = index;
10474         });
10475         
10476         return rowIndex;
10477     },
10478     /**
10479      * get the header TH element for columnIndex
10480      * @param {Number} columnIndex
10481      * @returns {Roo.Element}
10482      */
10483     getHeaderIndex: function(colIndex)
10484     {
10485         var cols = this.headEl.select('th', true).elements;
10486         return cols[colIndex]; 
10487     },
10488     /**
10489      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10490      * @param {domElement} cell to look for
10491      * @returns {Number} the column
10492      */
10493     getCellIndex : function(cell)
10494     {
10495         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10496         if(id){
10497             return parseInt(id[1], 10);
10498         }
10499         return 0;
10500     },
10501      /**
10502      * Returns the grid's underlying element = used by panel.Grid
10503      * @return {Element} The element
10504      */
10505     getGridEl : function(){
10506         return this.el;
10507     },
10508      /**
10509      * Forces a resize - used by panel.Grid
10510      * @return {Element} The element
10511      */
10512     autoSize : function()
10513     {
10514         if(this.disableAutoSize) {
10515             return;
10516         }
10517         //var ctr = Roo.get(this.container.dom.parentElement);
10518         var ctr = Roo.get(this.el.dom);
10519         
10520         var thd = this.getGridEl().select('thead',true).first();
10521         var tbd = this.getGridEl().select('tbody', true).first();
10522         var tfd = this.getGridEl().select('tfoot', true).first();
10523         
10524         var cw = ctr.getWidth();
10525         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10526         
10527         if (tbd) {
10528             
10529             tbd.setWidth(ctr.getWidth());
10530             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10531             // this needs fixing for various usage - currently only hydra job advers I think..
10532             //tdb.setHeight(
10533             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10534             //); 
10535             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10536             cw -= barsize;
10537         }
10538         cw = Math.max(cw, this.totalWidth);
10539         this.getGridEl().select('tbody tr',true).setWidth(cw);
10540         this.initCSS();
10541         
10542         // resize 'expandable coloumn?
10543         
10544         return; // we doe not have a view in this design..
10545         
10546     },
10547     onBodyScroll: function()
10548     {
10549         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10550         if(this.headEl){
10551             this.headEl.setStyle({
10552                 'position' : 'relative',
10553                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10554             });
10555         }
10556         
10557         if(this.lazyLoad){
10558             
10559             var scrollHeight = this.bodyEl.dom.scrollHeight;
10560             
10561             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10562             
10563             var height = this.bodyEl.getHeight();
10564             
10565             if(scrollHeight - height == scrollTop) {
10566                 
10567                 var total = this.ds.getTotalCount();
10568                 
10569                 if(this.footer.cursor + this.footer.pageSize < total){
10570                     
10571                     this.footer.ds.load({
10572                         params : {
10573                             start : this.footer.cursor + this.footer.pageSize,
10574                             limit : this.footer.pageSize
10575                         },
10576                         add : true
10577                     });
10578                 }
10579             }
10580             
10581         }
10582     },
10583     onColumnSplitterMoved : function(i, diff)
10584     {
10585         this.userResized = true;
10586         
10587         var cm = this.colModel;
10588         
10589         var w = this.getHeaderIndex(i).getWidth() + diff;
10590         
10591         
10592         cm.setColumnWidth(i, w, true);
10593         this.initCSS();
10594         //var cid = cm.getColumnId(i); << not used in this version?
10595        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10596         
10597         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10598         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10599         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10600 */
10601         //this.updateSplitters();
10602         //this.layout(); << ??
10603         this.fireEvent("columnresize", i, w);
10604     },
10605     onHeaderChange : function()
10606     {
10607         var header = this.renderHeader();
10608         var table = this.el.select('table', true).first();
10609         
10610         this.headEl.remove();
10611         this.headEl = table.createChild(header, this.bodyEl, false);
10612         
10613         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10614             e.on('click', this.sort, this);
10615         }, this);
10616         
10617         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10618             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10619         }
10620         
10621     },
10622     
10623     onHiddenChange : function(colModel, colIndex, hidden)
10624     {
10625         /*
10626         this.cm.setHidden()
10627         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10628         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10629         
10630         this.CSS.updateRule(thSelector, "display", "");
10631         this.CSS.updateRule(tdSelector, "display", "");
10632         
10633         if(hidden){
10634             this.CSS.updateRule(thSelector, "display", "none");
10635             this.CSS.updateRule(tdSelector, "display", "none");
10636         }
10637         */
10638         // onload calls initCSS()
10639         this.onHeaderChange();
10640         this.onLoad();
10641     },
10642     
10643     setColumnWidth: function(col_index, width)
10644     {
10645         // width = "md-2 xs-2..."
10646         if(!this.colModel.config[col_index]) {
10647             return;
10648         }
10649         
10650         var w = width.split(" ");
10651         
10652         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10653         
10654         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10655         
10656         
10657         for(var j = 0; j < w.length; j++) {
10658             
10659             if(!w[j]) {
10660                 continue;
10661             }
10662             
10663             var size_cls = w[j].split("-");
10664             
10665             if(!Number.isInteger(size_cls[1] * 1)) {
10666                 continue;
10667             }
10668             
10669             if(!this.colModel.config[col_index][size_cls[0]]) {
10670                 continue;
10671             }
10672             
10673             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10674                 continue;
10675             }
10676             
10677             h_row[0].classList.replace(
10678                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10679                 "col-"+size_cls[0]+"-"+size_cls[1]
10680             );
10681             
10682             for(var i = 0; i < rows.length; i++) {
10683                 
10684                 var size_cls = w[j].split("-");
10685                 
10686                 if(!Number.isInteger(size_cls[1] * 1)) {
10687                     continue;
10688                 }
10689                 
10690                 if(!this.colModel.config[col_index][size_cls[0]]) {
10691                     continue;
10692                 }
10693                 
10694                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10695                     continue;
10696                 }
10697                 
10698                 rows[i].classList.replace(
10699                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10700                     "col-"+size_cls[0]+"-"+size_cls[1]
10701                 );
10702             }
10703             
10704             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10705         }
10706     }
10707 });
10708
10709 // currently only used to find the split on drag.. 
10710 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10711
10712 /**
10713  * @depricated
10714 */
10715 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10716 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10717 /*
10718  * - LGPL
10719  *
10720  * table cell
10721  * 
10722  */
10723
10724 /**
10725  * @class Roo.bootstrap.TableCell
10726  * @extends Roo.bootstrap.Component
10727  * @children Roo.bootstrap.Component
10728  * @parent Roo.bootstrap.TableRow
10729  * Bootstrap TableCell class
10730  * 
10731  * @cfg {String} html cell contain text
10732  * @cfg {String} cls cell class
10733  * @cfg {String} tag cell tag (td|th) default td
10734  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10735  * @cfg {String} align Aligns the content in a cell
10736  * @cfg {String} axis Categorizes cells
10737  * @cfg {String} bgcolor Specifies the background color of a cell
10738  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10739  * @cfg {Number} colspan Specifies the number of columns a cell should span
10740  * @cfg {String} headers Specifies one or more header cells a cell is related to
10741  * @cfg {Number} height Sets the height of a cell
10742  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10743  * @cfg {Number} rowspan Sets the number of rows a cell should span
10744  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10745  * @cfg {String} valign Vertical aligns the content in a cell
10746  * @cfg {Number} width Specifies the width of a cell
10747  * 
10748  * @constructor
10749  * Create a new TableCell
10750  * @param {Object} config The config object
10751  */
10752
10753 Roo.bootstrap.TableCell = function(config){
10754     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10755 };
10756
10757 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10758     
10759     html: false,
10760     cls: false,
10761     tag: false,
10762     abbr: false,
10763     align: false,
10764     axis: false,
10765     bgcolor: false,
10766     charoff: false,
10767     colspan: false,
10768     headers: false,
10769     height: false,
10770     nowrap: false,
10771     rowspan: false,
10772     scope: false,
10773     valign: false,
10774     width: false,
10775     
10776     
10777     getAutoCreate : function(){
10778         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10779         
10780         cfg = {
10781             tag: 'td'
10782         };
10783         
10784         if(this.tag){
10785             cfg.tag = this.tag;
10786         }
10787         
10788         if (this.html) {
10789             cfg.html=this.html
10790         }
10791         if (this.cls) {
10792             cfg.cls=this.cls
10793         }
10794         if (this.abbr) {
10795             cfg.abbr=this.abbr
10796         }
10797         if (this.align) {
10798             cfg.align=this.align
10799         }
10800         if (this.axis) {
10801             cfg.axis=this.axis
10802         }
10803         if (this.bgcolor) {
10804             cfg.bgcolor=this.bgcolor
10805         }
10806         if (this.charoff) {
10807             cfg.charoff=this.charoff
10808         }
10809         if (this.colspan) {
10810             cfg.colspan=this.colspan
10811         }
10812         if (this.headers) {
10813             cfg.headers=this.headers
10814         }
10815         if (this.height) {
10816             cfg.height=this.height
10817         }
10818         if (this.nowrap) {
10819             cfg.nowrap=this.nowrap
10820         }
10821         if (this.rowspan) {
10822             cfg.rowspan=this.rowspan
10823         }
10824         if (this.scope) {
10825             cfg.scope=this.scope
10826         }
10827         if (this.valign) {
10828             cfg.valign=this.valign
10829         }
10830         if (this.width) {
10831             cfg.width=this.width
10832         }
10833         
10834         
10835         return cfg;
10836     }
10837    
10838 });
10839
10840  
10841
10842  /*
10843  * - LGPL
10844  *
10845  * table row
10846  * 
10847  */
10848
10849 /**
10850  * @class Roo.bootstrap.TableRow
10851  * @extends Roo.bootstrap.Component
10852  * @children Roo.bootstrap.TableCell
10853  * @parent Roo.bootstrap.TableBody
10854  * Bootstrap TableRow class
10855  * @cfg {String} cls row class
10856  * @cfg {String} align Aligns the content in a table row
10857  * @cfg {String} bgcolor Specifies a background color for a table row
10858  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10859  * @cfg {String} valign Vertical aligns the content in a table row
10860  * 
10861  * @constructor
10862  * Create a new TableRow
10863  * @param {Object} config The config object
10864  */
10865
10866 Roo.bootstrap.TableRow = function(config){
10867     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10868 };
10869
10870 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10871     
10872     cls: false,
10873     align: false,
10874     bgcolor: false,
10875     charoff: false,
10876     valign: false,
10877     
10878     getAutoCreate : function(){
10879         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10880         
10881         cfg = {
10882             tag: 'tr'
10883         };
10884             
10885         if(this.cls){
10886             cfg.cls = this.cls;
10887         }
10888         if(this.align){
10889             cfg.align = this.align;
10890         }
10891         if(this.bgcolor){
10892             cfg.bgcolor = this.bgcolor;
10893         }
10894         if(this.charoff){
10895             cfg.charoff = this.charoff;
10896         }
10897         if(this.valign){
10898             cfg.valign = this.valign;
10899         }
10900         
10901         return cfg;
10902     }
10903    
10904 });
10905
10906  
10907
10908  /*
10909  * - LGPL
10910  *
10911  * table body
10912  * 
10913  */
10914
10915 /**
10916  * @class Roo.bootstrap.TableBody
10917  * @extends Roo.bootstrap.Component
10918  * @children Roo.bootstrap.TableRow
10919  * @parent Roo.bootstrap.Table
10920  * Bootstrap TableBody class
10921  * @cfg {String} cls element class
10922  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10923  * @cfg {String} align Aligns the content inside the element
10924  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10925  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10926  * 
10927  * @constructor
10928  * Create a new TableBody
10929  * @param {Object} config The config object
10930  */
10931
10932 Roo.bootstrap.TableBody = function(config){
10933     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10934 };
10935
10936 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10937     
10938     cls: false,
10939     tag: false,
10940     align: false,
10941     charoff: false,
10942     valign: false,
10943     
10944     getAutoCreate : function(){
10945         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10946         
10947         cfg = {
10948             tag: 'tbody'
10949         };
10950             
10951         if (this.cls) {
10952             cfg.cls=this.cls
10953         }
10954         if(this.tag){
10955             cfg.tag = this.tag;
10956         }
10957         
10958         if(this.align){
10959             cfg.align = this.align;
10960         }
10961         if(this.charoff){
10962             cfg.charoff = this.charoff;
10963         }
10964         if(this.valign){
10965             cfg.valign = this.valign;
10966         }
10967         
10968         return cfg;
10969     }
10970     
10971     
10972 //    initEvents : function()
10973 //    {
10974 //        
10975 //        if(!this.store){
10976 //            return;
10977 //        }
10978 //        
10979 //        this.store = Roo.factory(this.store, Roo.data);
10980 //        this.store.on('load', this.onLoad, this);
10981 //        
10982 //        this.store.load();
10983 //        
10984 //    },
10985 //    
10986 //    onLoad: function () 
10987 //    {   
10988 //        this.fireEvent('load', this);
10989 //    }
10990 //    
10991 //   
10992 });
10993
10994  
10995
10996  /*
10997  * Based on:
10998  * Ext JS Library 1.1.1
10999  * Copyright(c) 2006-2007, Ext JS, LLC.
11000  *
11001  * Originally Released Under LGPL - original licence link has changed is not relivant.
11002  *
11003  * Fork - LGPL
11004  * <script type="text/javascript">
11005  */
11006
11007 // as we use this in bootstrap.
11008 Roo.namespace('Roo.form');
11009  /**
11010  * @class Roo.form.Action
11011  * Internal Class used to handle form actions
11012  * @constructor
11013  * @param {Roo.form.BasicForm} el The form element or its id
11014  * @param {Object} config Configuration options
11015  */
11016
11017  
11018  
11019 // define the action interface
11020 Roo.form.Action = function(form, options){
11021     this.form = form;
11022     this.options = options || {};
11023 };
11024 /**
11025  * Client Validation Failed
11026  * @const 
11027  */
11028 Roo.form.Action.CLIENT_INVALID = 'client';
11029 /**
11030  * Server Validation Failed
11031  * @const 
11032  */
11033 Roo.form.Action.SERVER_INVALID = 'server';
11034  /**
11035  * Connect to Server Failed
11036  * @const 
11037  */
11038 Roo.form.Action.CONNECT_FAILURE = 'connect';
11039 /**
11040  * Reading Data from Server Failed
11041  * @const 
11042  */
11043 Roo.form.Action.LOAD_FAILURE = 'load';
11044
11045 Roo.form.Action.prototype = {
11046     type : 'default',
11047     failureType : undefined,
11048     response : undefined,
11049     result : undefined,
11050
11051     // interface method
11052     run : function(options){
11053
11054     },
11055
11056     // interface method
11057     success : function(response){
11058
11059     },
11060
11061     // interface method
11062     handleResponse : function(response){
11063
11064     },
11065
11066     // default connection failure
11067     failure : function(response){
11068         
11069         this.response = response;
11070         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11071         this.form.afterAction(this, false);
11072     },
11073
11074     processResponse : function(response){
11075         this.response = response;
11076         if(!response.responseText){
11077             return true;
11078         }
11079         this.result = this.handleResponse(response);
11080         return this.result;
11081     },
11082
11083     // utility functions used internally
11084     getUrl : function(appendParams){
11085         var url = this.options.url || this.form.url || this.form.el.dom.action;
11086         if(appendParams){
11087             var p = this.getParams();
11088             if(p){
11089                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11090             }
11091         }
11092         return url;
11093     },
11094
11095     getMethod : function(){
11096         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11097     },
11098
11099     getParams : function(){
11100         var bp = this.form.baseParams;
11101         var p = this.options.params;
11102         if(p){
11103             if(typeof p == "object"){
11104                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11105             }else if(typeof p == 'string' && bp){
11106                 p += '&' + Roo.urlEncode(bp);
11107             }
11108         }else if(bp){
11109             p = Roo.urlEncode(bp);
11110         }
11111         return p;
11112     },
11113
11114     createCallback : function(){
11115         return {
11116             success: this.success,
11117             failure: this.failure,
11118             scope: this,
11119             timeout: (this.form.timeout*1000),
11120             upload: this.form.fileUpload ? this.success : undefined
11121         };
11122     }
11123 };
11124
11125 Roo.form.Action.Submit = function(form, options){
11126     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11127 };
11128
11129 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11130     type : 'submit',
11131
11132     haveProgress : false,
11133     uploadComplete : false,
11134     
11135     // uploadProgress indicator.
11136     uploadProgress : function()
11137     {
11138         if (!this.form.progressUrl) {
11139             return;
11140         }
11141         
11142         if (!this.haveProgress) {
11143             Roo.MessageBox.progress("Uploading", "Uploading");
11144         }
11145         if (this.uploadComplete) {
11146            Roo.MessageBox.hide();
11147            return;
11148         }
11149         
11150         this.haveProgress = true;
11151    
11152         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11153         
11154         var c = new Roo.data.Connection();
11155         c.request({
11156             url : this.form.progressUrl,
11157             params: {
11158                 id : uid
11159             },
11160             method: 'GET',
11161             success : function(req){
11162                //console.log(data);
11163                 var rdata = false;
11164                 var edata;
11165                 try  {
11166                    rdata = Roo.decode(req.responseText)
11167                 } catch (e) {
11168                     Roo.log("Invalid data from server..");
11169                     Roo.log(edata);
11170                     return;
11171                 }
11172                 if (!rdata || !rdata.success) {
11173                     Roo.log(rdata);
11174                     Roo.MessageBox.alert(Roo.encode(rdata));
11175                     return;
11176                 }
11177                 var data = rdata.data;
11178                 
11179                 if (this.uploadComplete) {
11180                    Roo.MessageBox.hide();
11181                    return;
11182                 }
11183                    
11184                 if (data){
11185                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11186                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11187                     );
11188                 }
11189                 this.uploadProgress.defer(2000,this);
11190             },
11191        
11192             failure: function(data) {
11193                 Roo.log('progress url failed ');
11194                 Roo.log(data);
11195             },
11196             scope : this
11197         });
11198            
11199     },
11200     
11201     
11202     run : function()
11203     {
11204         // run get Values on the form, so it syncs any secondary forms.
11205         this.form.getValues();
11206         
11207         var o = this.options;
11208         var method = this.getMethod();
11209         var isPost = method == 'POST';
11210         if(o.clientValidation === false || this.form.isValid()){
11211             
11212             if (this.form.progressUrl) {
11213                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11214                     (new Date() * 1) + '' + Math.random());
11215                     
11216             } 
11217             
11218             
11219             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11220                 form:this.form.el.dom,
11221                 url:this.getUrl(!isPost),
11222                 method: method,
11223                 params:isPost ? this.getParams() : null,
11224                 isUpload: this.form.fileUpload,
11225                 formData : this.form.formData
11226             }));
11227             
11228             this.uploadProgress();
11229
11230         }else if (o.clientValidation !== false){ // client validation failed
11231             this.failureType = Roo.form.Action.CLIENT_INVALID;
11232             this.form.afterAction(this, false);
11233         }
11234     },
11235
11236     success : function(response)
11237     {
11238         this.uploadComplete= true;
11239         if (this.haveProgress) {
11240             Roo.MessageBox.hide();
11241         }
11242         
11243         
11244         var result = this.processResponse(response);
11245         if(result === true || result.success){
11246             this.form.afterAction(this, true);
11247             return;
11248         }
11249         if(result.errors){
11250             this.form.markInvalid(result.errors);
11251             this.failureType = Roo.form.Action.SERVER_INVALID;
11252         }
11253         this.form.afterAction(this, false);
11254     },
11255     failure : function(response)
11256     {
11257         this.uploadComplete= true;
11258         if (this.haveProgress) {
11259             Roo.MessageBox.hide();
11260         }
11261         
11262         this.response = response;
11263         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11264         this.form.afterAction(this, false);
11265     },
11266     
11267     handleResponse : function(response){
11268         if(this.form.errorReader){
11269             var rs = this.form.errorReader.read(response);
11270             var errors = [];
11271             if(rs.records){
11272                 for(var i = 0, len = rs.records.length; i < len; i++) {
11273                     var r = rs.records[i];
11274                     errors[i] = r.data;
11275                 }
11276             }
11277             if(errors.length < 1){
11278                 errors = null;
11279             }
11280             return {
11281                 success : rs.success,
11282                 errors : errors
11283             };
11284         }
11285         var ret = false;
11286         try {
11287             ret = Roo.decode(response.responseText);
11288         } catch (e) {
11289             ret = {
11290                 success: false,
11291                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11292                 errors : []
11293             };
11294         }
11295         return ret;
11296         
11297     }
11298 });
11299
11300
11301 Roo.form.Action.Load = function(form, options){
11302     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11303     this.reader = this.form.reader;
11304 };
11305
11306 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11307     type : 'load',
11308
11309     run : function(){
11310         
11311         Roo.Ajax.request(Roo.apply(
11312                 this.createCallback(), {
11313                     method:this.getMethod(),
11314                     url:this.getUrl(false),
11315                     params:this.getParams()
11316         }));
11317     },
11318
11319     success : function(response){
11320         
11321         var result = this.processResponse(response);
11322         if(result === true || !result.success || !result.data){
11323             this.failureType = Roo.form.Action.LOAD_FAILURE;
11324             this.form.afterAction(this, false);
11325             return;
11326         }
11327         this.form.clearInvalid();
11328         this.form.setValues(result.data);
11329         this.form.afterAction(this, true);
11330     },
11331
11332     handleResponse : function(response){
11333         if(this.form.reader){
11334             var rs = this.form.reader.read(response);
11335             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11336             return {
11337                 success : rs.success,
11338                 data : data
11339             };
11340         }
11341         return Roo.decode(response.responseText);
11342     }
11343 });
11344
11345 Roo.form.Action.ACTION_TYPES = {
11346     'load' : Roo.form.Action.Load,
11347     'submit' : Roo.form.Action.Submit
11348 };/*
11349  * - LGPL
11350  *
11351  * form
11352  *
11353  */
11354
11355 /**
11356  * @class Roo.bootstrap.form.Form
11357  * @extends Roo.bootstrap.Component
11358  * @children Roo.bootstrap.Component
11359  * Bootstrap Form class
11360  * @cfg {String} method  GET | POST (default POST)
11361  * @cfg {String} labelAlign top | left (default top)
11362  * @cfg {String} align left  | right - for navbars
11363  * @cfg {Boolean} loadMask load mask when submit (default true)
11364
11365  *
11366  * @constructor
11367  * Create a new Form
11368  * @param {Object} config The config object
11369  */
11370
11371
11372 Roo.bootstrap.form.Form = function(config){
11373     
11374     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11375     
11376     Roo.bootstrap.form.Form.popover.apply();
11377     
11378     this.addEvents({
11379         /**
11380          * @event clientvalidation
11381          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11382          * @param {Form} this
11383          * @param {Boolean} valid true if the form has passed client-side validation
11384          */
11385         clientvalidation: true,
11386         /**
11387          * @event beforeaction
11388          * Fires before any action is performed. Return false to cancel the action.
11389          * @param {Form} this
11390          * @param {Action} action The action to be performed
11391          */
11392         beforeaction: true,
11393         /**
11394          * @event actionfailed
11395          * Fires when an action fails.
11396          * @param {Form} this
11397          * @param {Action} action The action that failed
11398          */
11399         actionfailed : true,
11400         /**
11401          * @event actioncomplete
11402          * Fires when an action is completed.
11403          * @param {Form} this
11404          * @param {Action} action The action that completed
11405          */
11406         actioncomplete : true
11407     });
11408 };
11409
11410 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11411
11412      /**
11413      * @cfg {String} method
11414      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11415      */
11416     method : 'POST',
11417     /**
11418      * @cfg {String} url
11419      * The URL to use for form actions if one isn't supplied in the action options.
11420      */
11421     /**
11422      * @cfg {Boolean} fileUpload
11423      * Set to true if this form is a file upload.
11424      */
11425
11426     /**
11427      * @cfg {Object} baseParams
11428      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11429      */
11430
11431     /**
11432      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11433      */
11434     timeout: 30,
11435     /**
11436      * @cfg {Sting} align (left|right) for navbar forms
11437      */
11438     align : 'left',
11439
11440     // private
11441     activeAction : null,
11442
11443     /**
11444      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11445      * element by passing it or its id or mask the form itself by passing in true.
11446      * @type Mixed
11447      */
11448     waitMsgTarget : false,
11449
11450     loadMask : true,
11451     
11452     /**
11453      * @cfg {Boolean} errorMask (true|false) default false
11454      */
11455     errorMask : false,
11456     
11457     /**
11458      * @cfg {Number} maskOffset Default 100
11459      */
11460     maskOffset : 100,
11461     
11462     /**
11463      * @cfg {Boolean} maskBody
11464      */
11465     maskBody : false,
11466
11467     getAutoCreate : function(){
11468
11469         var cfg = {
11470             tag: 'form',
11471             method : this.method || 'POST',
11472             id : this.id || Roo.id(),
11473             cls : ''
11474         };
11475         if (this.parent().xtype.match(/^Nav/)) {
11476             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11477
11478         }
11479
11480         if (this.labelAlign == 'left' ) {
11481             cfg.cls += ' form-horizontal';
11482         }
11483
11484
11485         return cfg;
11486     },
11487     initEvents : function()
11488     {
11489         this.el.on('submit', this.onSubmit, this);
11490         // this was added as random key presses on the form where triggering form submit.
11491         this.el.on('keypress', function(e) {
11492             if (e.getCharCode() != 13) {
11493                 return true;
11494             }
11495             // we might need to allow it for textareas.. and some other items.
11496             // check e.getTarget().
11497
11498             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11499                 return true;
11500             }
11501
11502             Roo.log("keypress blocked");
11503
11504             e.preventDefault();
11505             return false;
11506         });
11507         
11508     },
11509     // private
11510     onSubmit : function(e){
11511         e.stopEvent();
11512     },
11513
11514      /**
11515      * Returns true if client-side validation on the form is successful.
11516      * @return Boolean
11517      */
11518     isValid : function(){
11519         var items = this.getItems();
11520         var valid = true;
11521         var target = false;
11522         
11523         items.each(function(f){
11524             
11525             if(f.validate()){
11526                 return;
11527             }
11528             
11529             Roo.log('invalid field: ' + f.name);
11530             
11531             valid = false;
11532
11533             if(!target && f.el.isVisible(true)){
11534                 target = f;
11535             }
11536            
11537         });
11538         
11539         if(this.errorMask && !valid){
11540             Roo.bootstrap.form.Form.popover.mask(this, target);
11541         }
11542         
11543         return valid;
11544     },
11545     
11546     /**
11547      * Returns true if any fields in this form have changed since their original load.
11548      * @return Boolean
11549      */
11550     isDirty : function(){
11551         var dirty = false;
11552         var items = this.getItems();
11553         items.each(function(f){
11554            if(f.isDirty()){
11555                dirty = true;
11556                return false;
11557            }
11558            return true;
11559         });
11560         return dirty;
11561     },
11562      /**
11563      * Performs a predefined action (submit or load) or custom actions you define on this form.
11564      * @param {String} actionName The name of the action type
11565      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11566      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11567      * accept other config options):
11568      * <pre>
11569 Property          Type             Description
11570 ----------------  ---------------  ----------------------------------------------------------------------------------
11571 url               String           The url for the action (defaults to the form's url)
11572 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11573 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11574 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11575                                    validate the form on the client (defaults to false)
11576      * </pre>
11577      * @return {BasicForm} this
11578      */
11579     doAction : function(action, options){
11580         if(typeof action == 'string'){
11581             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11582         }
11583         if(this.fireEvent('beforeaction', this, action) !== false){
11584             this.beforeAction(action);
11585             action.run.defer(100, action);
11586         }
11587         return this;
11588     },
11589
11590     // private
11591     beforeAction : function(action){
11592         var o = action.options;
11593         
11594         if(this.loadMask){
11595             
11596             if(this.maskBody){
11597                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11598             } else {
11599                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11600             }
11601         }
11602         // not really supported yet.. ??
11603
11604         //if(this.waitMsgTarget === true){
11605         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11606         //}else if(this.waitMsgTarget){
11607         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11608         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11609         //}else {
11610         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11611        // }
11612
11613     },
11614
11615     // private
11616     afterAction : function(action, success){
11617         this.activeAction = null;
11618         var o = action.options;
11619
11620         if(this.loadMask){
11621             
11622             if(this.maskBody){
11623                 Roo.get(document.body).unmask();
11624             } else {
11625                 this.el.unmask();
11626             }
11627         }
11628         
11629         //if(this.waitMsgTarget === true){
11630 //            this.el.unmask();
11631         //}else if(this.waitMsgTarget){
11632         //    this.waitMsgTarget.unmask();
11633         //}else{
11634         //    Roo.MessageBox.updateProgress(1);
11635         //    Roo.MessageBox.hide();
11636        // }
11637         //
11638         if(success){
11639             if(o.reset){
11640                 this.reset();
11641             }
11642             Roo.callback(o.success, o.scope, [this, action]);
11643             this.fireEvent('actioncomplete', this, action);
11644
11645         }else{
11646
11647             // failure condition..
11648             // we have a scenario where updates need confirming.
11649             // eg. if a locking scenario exists..
11650             // we look for { errors : { needs_confirm : true }} in the response.
11651             if (
11652                 (typeof(action.result) != 'undefined')  &&
11653                 (typeof(action.result.errors) != 'undefined')  &&
11654                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11655            ){
11656                 var _t = this;
11657                 Roo.log("not supported yet");
11658                  /*
11659
11660                 Roo.MessageBox.confirm(
11661                     "Change requires confirmation",
11662                     action.result.errorMsg,
11663                     function(r) {
11664                         if (r != 'yes') {
11665                             return;
11666                         }
11667                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11668                     }
11669
11670                 );
11671                 */
11672
11673
11674                 return;
11675             }
11676
11677             Roo.callback(o.failure, o.scope, [this, action]);
11678             // show an error message if no failed handler is set..
11679             if (!this.hasListener('actionfailed')) {
11680                 Roo.log("need to add dialog support");
11681                 /*
11682                 Roo.MessageBox.alert("Error",
11683                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11684                         action.result.errorMsg :
11685                         "Saving Failed, please check your entries or try again"
11686                 );
11687                 */
11688             }
11689
11690             this.fireEvent('actionfailed', this, action);
11691         }
11692
11693     },
11694     /**
11695      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11696      * @param {String} id The value to search for
11697      * @return Field
11698      */
11699     findField : function(id){
11700         var items = this.getItems();
11701         var field = items.get(id);
11702         if(!field){
11703              items.each(function(f){
11704                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11705                     field = f;
11706                     return false;
11707                 }
11708                 return true;
11709             });
11710         }
11711         return field || null;
11712     },
11713      /**
11714      * Mark fields in this form invalid in bulk.
11715      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11716      * @return {BasicForm} this
11717      */
11718     markInvalid : function(errors){
11719         if(errors instanceof Array){
11720             for(var i = 0, len = errors.length; i < len; i++){
11721                 var fieldError = errors[i];
11722                 var f = this.findField(fieldError.id);
11723                 if(f){
11724                     f.markInvalid(fieldError.msg);
11725                 }
11726             }
11727         }else{
11728             var field, id;
11729             for(id in errors){
11730                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11731                     field.markInvalid(errors[id]);
11732                 }
11733             }
11734         }
11735         //Roo.each(this.childForms || [], function (f) {
11736         //    f.markInvalid(errors);
11737         //});
11738
11739         return this;
11740     },
11741
11742     /**
11743      * Set values for fields in this form in bulk.
11744      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11745      * @return {BasicForm} this
11746      */
11747     setValues : function(values){
11748         if(values instanceof Array){ // array of objects
11749             for(var i = 0, len = values.length; i < len; i++){
11750                 var v = values[i];
11751                 var f = this.findField(v.id);
11752                 if(f){
11753                     f.setValue(v.value);
11754                     if(this.trackResetOnLoad){
11755                         f.originalValue = f.getValue();
11756                     }
11757                 }
11758             }
11759         }else{ // object hash
11760             var field, id;
11761             for(id in values){
11762                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11763
11764                     if (field.setFromData &&
11765                         field.valueField &&
11766                         field.displayField &&
11767                         // combos' with local stores can
11768                         // be queried via setValue()
11769                         // to set their value..
11770                         (field.store && !field.store.isLocal)
11771                         ) {
11772                         // it's a combo
11773                         var sd = { };
11774                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11775                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11776                         field.setFromData(sd);
11777
11778                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11779                         
11780                         field.setFromData(values);
11781                         
11782                     } else {
11783                         field.setValue(values[id]);
11784                     }
11785
11786
11787                     if(this.trackResetOnLoad){
11788                         field.originalValue = field.getValue();
11789                     }
11790                 }
11791             }
11792         }
11793
11794         //Roo.each(this.childForms || [], function (f) {
11795         //    f.setValues(values);
11796         //});
11797
11798         return this;
11799     },
11800
11801     /**
11802      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11803      * they are returned as an array.
11804      * @param {Boolean} asString
11805      * @return {Object}
11806      */
11807     getValues : function(asString){
11808         //if (this.childForms) {
11809             // copy values from the child forms
11810         //    Roo.each(this.childForms, function (f) {
11811         //        this.setValues(f.getValues());
11812         //    }, this);
11813         //}
11814
11815
11816
11817         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11818         if(asString === true){
11819             return fs;
11820         }
11821         return Roo.urlDecode(fs);
11822     },
11823
11824     /**
11825      * Returns the fields in this form as an object with key/value pairs.
11826      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11827      * @return {Object}
11828      */
11829     getFieldValues : function(with_hidden)
11830     {
11831         var items = this.getItems();
11832         var ret = {};
11833         items.each(function(f){
11834             
11835             if (!f.getName()) {
11836                 return;
11837             }
11838             
11839             var v = f.getValue();
11840             
11841             if (f.inputType =='radio') {
11842                 if (typeof(ret[f.getName()]) == 'undefined') {
11843                     ret[f.getName()] = ''; // empty..
11844                 }
11845
11846                 if (!f.el.dom.checked) {
11847                     return;
11848
11849                 }
11850                 v = f.el.dom.value;
11851
11852             }
11853             
11854             if(f.xtype == 'MoneyField'){
11855                 ret[f.currencyName] = f.getCurrency();
11856             }
11857
11858             // not sure if this supported any more..
11859             if ((typeof(v) == 'object') && f.getRawValue) {
11860                 v = f.getRawValue() ; // dates..
11861             }
11862             // combo boxes where name != hiddenName...
11863             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11864                 ret[f.name] = f.getRawValue();
11865             }
11866             ret[f.getName()] = v;
11867         });
11868
11869         return ret;
11870     },
11871
11872     /**
11873      * Clears all invalid messages in this form.
11874      * @return {BasicForm} this
11875      */
11876     clearInvalid : function(){
11877         var items = this.getItems();
11878
11879         items.each(function(f){
11880            f.clearInvalid();
11881         });
11882
11883         return this;
11884     },
11885
11886     /**
11887      * Resets this form.
11888      * @return {BasicForm} this
11889      */
11890     reset : function(){
11891         var items = this.getItems();
11892         items.each(function(f){
11893             f.reset();
11894         });
11895
11896         Roo.each(this.childForms || [], function (f) {
11897             f.reset();
11898         });
11899
11900
11901         return this;
11902     },
11903     
11904     getItems : function()
11905     {
11906         var r=new Roo.util.MixedCollection(false, function(o){
11907             return o.id || (o.id = Roo.id());
11908         });
11909         var iter = function(el) {
11910             if (el.inputEl) {
11911                 r.add(el);
11912             }
11913             if (!el.items) {
11914                 return;
11915             }
11916             Roo.each(el.items,function(e) {
11917                 iter(e);
11918             });
11919         };
11920
11921         iter(this);
11922         return r;
11923     },
11924     
11925     hideFields : function(items)
11926     {
11927         Roo.each(items, function(i){
11928             
11929             var f = this.findField(i);
11930             
11931             if(!f){
11932                 return;
11933             }
11934             
11935             f.hide();
11936             
11937         }, this);
11938     },
11939     
11940     showFields : function(items)
11941     {
11942         Roo.each(items, function(i){
11943             
11944             var f = this.findField(i);
11945             
11946             if(!f){
11947                 return;
11948             }
11949             
11950             f.show();
11951             
11952         }, this);
11953     }
11954
11955 });
11956
11957 Roo.apply(Roo.bootstrap.form.Form, {
11958     
11959     popover : {
11960         
11961         padding : 5,
11962         
11963         isApplied : false,
11964         
11965         isMasked : false,
11966         
11967         form : false,
11968         
11969         target : false,
11970         
11971         toolTip : false,
11972         
11973         intervalID : false,
11974         
11975         maskEl : false,
11976         
11977         apply : function()
11978         {
11979             if(this.isApplied){
11980                 return;
11981             }
11982             
11983             this.maskEl = {
11984                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11985                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11986                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11987                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11988             };
11989             
11990             this.maskEl.top.enableDisplayMode("block");
11991             this.maskEl.left.enableDisplayMode("block");
11992             this.maskEl.bottom.enableDisplayMode("block");
11993             this.maskEl.right.enableDisplayMode("block");
11994             
11995             this.toolTip = new Roo.bootstrap.Tooltip({
11996                 cls : 'roo-form-error-popover',
11997                 alignment : {
11998                     'left' : ['r-l', [-2,0], 'right'],
11999                     'right' : ['l-r', [2,0], 'left'],
12000                     'bottom' : ['tl-bl', [0,2], 'top'],
12001                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12002                 }
12003             });
12004             
12005             this.toolTip.render(Roo.get(document.body));
12006
12007             this.toolTip.el.enableDisplayMode("block");
12008             
12009             Roo.get(document.body).on('click', function(){
12010                 this.unmask();
12011             }, this);
12012             
12013             Roo.get(document.body).on('touchstart', function(){
12014                 this.unmask();
12015             }, this);
12016             
12017             this.isApplied = true
12018         },
12019         
12020         mask : function(form, target)
12021         {
12022             this.form = form;
12023             
12024             this.target = target;
12025             
12026             if(!this.form.errorMask || !target.el){
12027                 return;
12028             }
12029             
12030             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12031             
12032             Roo.log(scrollable);
12033             
12034             var ot = this.target.el.calcOffsetsTo(scrollable);
12035             
12036             var scrollTo = ot[1] - this.form.maskOffset;
12037             
12038             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12039             
12040             scrollable.scrollTo('top', scrollTo);
12041             
12042             var box = this.target.el.getBox();
12043             Roo.log(box);
12044             var zIndex = Roo.bootstrap.Modal.zIndex++;
12045
12046             
12047             this.maskEl.top.setStyle('position', 'absolute');
12048             this.maskEl.top.setStyle('z-index', zIndex);
12049             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12050             this.maskEl.top.setLeft(0);
12051             this.maskEl.top.setTop(0);
12052             this.maskEl.top.show();
12053             
12054             this.maskEl.left.setStyle('position', 'absolute');
12055             this.maskEl.left.setStyle('z-index', zIndex);
12056             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12057             this.maskEl.left.setLeft(0);
12058             this.maskEl.left.setTop(box.y - this.padding);
12059             this.maskEl.left.show();
12060
12061             this.maskEl.bottom.setStyle('position', 'absolute');
12062             this.maskEl.bottom.setStyle('z-index', zIndex);
12063             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12064             this.maskEl.bottom.setLeft(0);
12065             this.maskEl.bottom.setTop(box.bottom + this.padding);
12066             this.maskEl.bottom.show();
12067
12068             this.maskEl.right.setStyle('position', 'absolute');
12069             this.maskEl.right.setStyle('z-index', zIndex);
12070             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12071             this.maskEl.right.setLeft(box.right + this.padding);
12072             this.maskEl.right.setTop(box.y - this.padding);
12073             this.maskEl.right.show();
12074
12075             this.toolTip.bindEl = this.target.el;
12076
12077             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12078
12079             var tip = this.target.blankText;
12080
12081             if(this.target.getValue() !== '' ) {
12082                 
12083                 if (this.target.invalidText.length) {
12084                     tip = this.target.invalidText;
12085                 } else if (this.target.regexText.length){
12086                     tip = this.target.regexText;
12087                 }
12088             }
12089
12090             this.toolTip.show(tip);
12091
12092             this.intervalID = window.setInterval(function() {
12093                 Roo.bootstrap.form.Form.popover.unmask();
12094             }, 10000);
12095
12096             window.onwheel = function(){ return false;};
12097             
12098             (function(){ this.isMasked = true; }).defer(500, this);
12099             
12100         },
12101         
12102         unmask : function()
12103         {
12104             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12105                 return;
12106             }
12107             
12108             this.maskEl.top.setStyle('position', 'absolute');
12109             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12110             this.maskEl.top.hide();
12111
12112             this.maskEl.left.setStyle('position', 'absolute');
12113             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12114             this.maskEl.left.hide();
12115
12116             this.maskEl.bottom.setStyle('position', 'absolute');
12117             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12118             this.maskEl.bottom.hide();
12119
12120             this.maskEl.right.setStyle('position', 'absolute');
12121             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12122             this.maskEl.right.hide();
12123             
12124             this.toolTip.hide();
12125             
12126             this.toolTip.el.hide();
12127             
12128             window.onwheel = function(){ return true;};
12129             
12130             if(this.intervalID){
12131                 window.clearInterval(this.intervalID);
12132                 this.intervalID = false;
12133             }
12134             
12135             this.isMasked = false;
12136             
12137         }
12138         
12139     }
12140     
12141 });
12142
12143 /*
12144  * Based on:
12145  * Ext JS Library 1.1.1
12146  * Copyright(c) 2006-2007, Ext JS, LLC.
12147  *
12148  * Originally Released Under LGPL - original licence link has changed is not relivant.
12149  *
12150  * Fork - LGPL
12151  * <script type="text/javascript">
12152  */
12153 /**
12154  * @class Roo.form.VTypes
12155  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12156  * @static
12157  */
12158 Roo.form.VTypes = function(){
12159     // closure these in so they are only created once.
12160     var alpha = /^[a-zA-Z_]+$/;
12161     var alphanum = /^[a-zA-Z0-9_]+$/;
12162     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12163     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12164
12165     // All these messages and functions are configurable
12166     return {
12167         /**
12168          * The function used to validate email addresses
12169          * @param {String} value The email address
12170          */
12171         email : function(v){
12172             return email.test(v);
12173         },
12174         /**
12175          * The error text to display when the email validation function returns false
12176          * @type String
12177          */
12178         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12179         /**
12180          * The keystroke filter mask to be applied on email input
12181          * @type RegExp
12182          */
12183         emailMask : /[a-z0-9_\.\-@]/i,
12184
12185         /**
12186          * The function used to validate URLs
12187          * @param {String} value The URL
12188          */
12189         url : function(v){
12190             return url.test(v);
12191         },
12192         /**
12193          * The error text to display when the url validation function returns false
12194          * @type String
12195          */
12196         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12197         
12198         /**
12199          * The function used to validate alpha values
12200          * @param {String} value The value
12201          */
12202         alpha : function(v){
12203             return alpha.test(v);
12204         },
12205         /**
12206          * The error text to display when the alpha validation function returns false
12207          * @type String
12208          */
12209         alphaText : 'This field should only contain letters and _',
12210         /**
12211          * The keystroke filter mask to be applied on alpha input
12212          * @type RegExp
12213          */
12214         alphaMask : /[a-z_]/i,
12215
12216         /**
12217          * The function used to validate alphanumeric values
12218          * @param {String} value The value
12219          */
12220         alphanum : function(v){
12221             return alphanum.test(v);
12222         },
12223         /**
12224          * The error text to display when the alphanumeric validation function returns false
12225          * @type String
12226          */
12227         alphanumText : 'This field should only contain letters, numbers and _',
12228         /**
12229          * The keystroke filter mask to be applied on alphanumeric input
12230          * @type RegExp
12231          */
12232         alphanumMask : /[a-z0-9_]/i
12233     };
12234 }();/*
12235  * - LGPL
12236  *
12237  * Input
12238  * 
12239  */
12240
12241 /**
12242  * @class Roo.bootstrap.form.Input
12243  * @extends Roo.bootstrap.Component
12244  * Bootstrap Input class
12245  * @cfg {Boolean} disabled is it disabled
12246  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12247  * @cfg {String} name name of the input
12248  * @cfg {string} fieldLabel - the label associated
12249  * @cfg {string} placeholder - placeholder to put in text.
12250  * @cfg {string} before - input group add on before
12251  * @cfg {string} after - input group add on after
12252  * @cfg {string} size - (lg|sm) or leave empty..
12253  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12254  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12255  * @cfg {Number} md colspan out of 12 for computer-sized screens
12256  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12257  * @cfg {string} value default value of the input
12258  * @cfg {Number} labelWidth set the width of label 
12259  * @cfg {Number} labellg set the width of label (1-12)
12260  * @cfg {Number} labelmd set the width of label (1-12)
12261  * @cfg {Number} labelsm set the width of label (1-12)
12262  * @cfg {Number} labelxs set the width of label (1-12)
12263  * @cfg {String} labelAlign (top|left)
12264  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12265  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12266  * @cfg {String} indicatorpos (left|right) default left
12267  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12268  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12269  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12270  * @cfg {Roo.bootstrap.Button} before Button to show before
12271  * @cfg {Roo.bootstrap.Button} afterButton to show before
12272  * @cfg {String} align (left|center|right) Default left
12273  * @cfg {Boolean} forceFeedback (true|false) Default false
12274  * 
12275  * @constructor
12276  * Create a new Input
12277  * @param {Object} config The config object
12278  */
12279
12280 Roo.bootstrap.form.Input = function(config){
12281     
12282     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12283     
12284     this.addEvents({
12285         /**
12286          * @event focus
12287          * Fires when this field receives input focus.
12288          * @param {Roo.form.Field} this
12289          */
12290         focus : true,
12291         /**
12292          * @event blur
12293          * Fires when this field loses input focus.
12294          * @param {Roo.form.Field} this
12295          */
12296         blur : true,
12297         /**
12298          * @event specialkey
12299          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12300          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12301          * @param {Roo.form.Field} this
12302          * @param {Roo.EventObject} e The event object
12303          */
12304         specialkey : true,
12305         /**
12306          * @event change
12307          * Fires just before the field blurs if the field value has changed.
12308          * @param {Roo.form.Field} this
12309          * @param {Mixed} newValue The new value
12310          * @param {Mixed} oldValue The original value
12311          */
12312         change : true,
12313         /**
12314          * @event invalid
12315          * Fires after the field has been marked as invalid.
12316          * @param {Roo.form.Field} this
12317          * @param {String} msg The validation message
12318          */
12319         invalid : true,
12320         /**
12321          * @event valid
12322          * Fires after the field has been validated with no errors.
12323          * @param {Roo.form.Field} this
12324          */
12325         valid : true,
12326          /**
12327          * @event keyup
12328          * Fires after the key up
12329          * @param {Roo.form.Field} this
12330          * @param {Roo.EventObject}  e The event Object
12331          */
12332         keyup : true,
12333         /**
12334          * @event paste
12335          * Fires after the user pastes into input
12336          * @param {Roo.form.Field} this
12337          * @param {Roo.EventObject}  e The event Object
12338          */
12339         paste : true
12340     });
12341 };
12342
12343 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12344      /**
12345      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12346       automatic validation (defaults to "keyup").
12347      */
12348     validationEvent : "keyup",
12349      /**
12350      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12351      */
12352     validateOnBlur : true,
12353     /**
12354      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12355      */
12356     validationDelay : 250,
12357      /**
12358      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12359      */
12360     focusClass : "x-form-focus",  // not needed???
12361     
12362        
12363     /**
12364      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12365      */
12366     invalidClass : "has-warning",
12367     
12368     /**
12369      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12370      */
12371     validClass : "has-success",
12372     
12373     /**
12374      * @cfg {Boolean} hasFeedback (true|false) default true
12375      */
12376     hasFeedback : true,
12377     
12378     /**
12379      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12380      */
12381     invalidFeedbackClass : "glyphicon-warning-sign",
12382     
12383     /**
12384      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12385      */
12386     validFeedbackClass : "glyphicon-ok",
12387     
12388     /**
12389      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12390      */
12391     selectOnFocus : false,
12392     
12393      /**
12394      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12395      */
12396     maskRe : null,
12397        /**
12398      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12399      */
12400     vtype : null,
12401     
12402       /**
12403      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12404      */
12405     disableKeyFilter : false,
12406     
12407        /**
12408      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12409      */
12410     disabled : false,
12411      /**
12412      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12413      */
12414     allowBlank : true,
12415     /**
12416      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12417      */
12418     blankText : "Please complete this mandatory field",
12419     
12420      /**
12421      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12422      */
12423     minLength : 0,
12424     /**
12425      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12426      */
12427     maxLength : Number.MAX_VALUE,
12428     /**
12429      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12430      */
12431     minLengthText : "The minimum length for this field is {0}",
12432     /**
12433      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12434      */
12435     maxLengthText : "The maximum length for this field is {0}",
12436   
12437     
12438     /**
12439      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12440      * If available, this function will be called only after the basic validators all return true, and will be passed the
12441      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12442      */
12443     validator : null,
12444     /**
12445      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12446      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12447      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12448      */
12449     regex : null,
12450     /**
12451      * @cfg {String} regexText -- Depricated - use Invalid Text
12452      */
12453     regexText : "",
12454     
12455     /**
12456      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12457      */
12458     invalidText : "",
12459     
12460     
12461     
12462     autocomplete: false,
12463     
12464     
12465     fieldLabel : '',
12466     inputType : 'text',
12467     
12468     name : false,
12469     placeholder: false,
12470     before : false,
12471     after : false,
12472     size : false,
12473     hasFocus : false,
12474     preventMark: false,
12475     isFormField : true,
12476     value : '',
12477     labelWidth : 2,
12478     labelAlign : false,
12479     readOnly : false,
12480     align : false,
12481     formatedValue : false,
12482     forceFeedback : false,
12483     
12484     indicatorpos : 'left',
12485     
12486     labellg : 0,
12487     labelmd : 0,
12488     labelsm : 0,
12489     labelxs : 0,
12490     
12491     capture : '',
12492     accept : '',
12493     
12494     parentLabelAlign : function()
12495     {
12496         var parent = this;
12497         while (parent.parent()) {
12498             parent = parent.parent();
12499             if (typeof(parent.labelAlign) !='undefined') {
12500                 return parent.labelAlign;
12501             }
12502         }
12503         return 'left';
12504         
12505     },
12506     
12507     getAutoCreate : function()
12508     {
12509         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12510         
12511         var id = Roo.id();
12512         
12513         var cfg = {};
12514         
12515         if(this.inputType != 'hidden'){
12516             cfg.cls = 'form-group' //input-group
12517         }
12518         
12519         var input =  {
12520             tag: 'input',
12521             id : id,
12522             type : this.inputType,
12523             value : this.value,
12524             cls : 'form-control',
12525             placeholder : this.placeholder || '',
12526             autocomplete : this.autocomplete || 'new-password'
12527         };
12528         if (this.inputType == 'file') {
12529             input.style = 'overflow:hidden'; // why not in CSS?
12530         }
12531         
12532         if(this.capture.length){
12533             input.capture = this.capture;
12534         }
12535         
12536         if(this.accept.length){
12537             input.accept = this.accept + "/*";
12538         }
12539         
12540         if(this.align){
12541             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12542         }
12543         
12544         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12545             input.maxLength = this.maxLength;
12546         }
12547         
12548         if (this.disabled) {
12549             input.disabled=true;
12550         }
12551         
12552         if (this.readOnly) {
12553             input.readonly=true;
12554         }
12555         
12556         if (this.name) {
12557             input.name = this.name;
12558         }
12559         
12560         if (this.size) {
12561             input.cls += ' input-' + this.size;
12562         }
12563         
12564         var settings=this;
12565         ['xs','sm','md','lg'].map(function(size){
12566             if (settings[size]) {
12567                 cfg.cls += ' col-' + size + '-' + settings[size];
12568             }
12569         });
12570         
12571         var inputblock = input;
12572         
12573         var feedback = {
12574             tag: 'span',
12575             cls: 'glyphicon form-control-feedback'
12576         };
12577             
12578         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12579             
12580             inputblock = {
12581                 cls : 'has-feedback',
12582                 cn :  [
12583                     input,
12584                     feedback
12585                 ] 
12586             };  
12587         }
12588         
12589         if (this.before || this.after) {
12590             
12591             inputblock = {
12592                 cls : 'input-group',
12593                 cn :  [] 
12594             };
12595             
12596             if (this.before && typeof(this.before) == 'string') {
12597                 
12598                 inputblock.cn.push({
12599                     tag :'span',
12600                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12601                     html : this.before
12602                 });
12603             }
12604             if (this.before && typeof(this.before) == 'object') {
12605                 this.before = Roo.factory(this.before);
12606                 
12607                 inputblock.cn.push({
12608                     tag :'span',
12609                     cls : 'roo-input-before input-group-prepend   input-group-' +
12610                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12611                 });
12612             }
12613             
12614             inputblock.cn.push(input);
12615             
12616             if (this.after && typeof(this.after) == 'string') {
12617                 inputblock.cn.push({
12618                     tag :'span',
12619                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12620                     html : this.after
12621                 });
12622             }
12623             if (this.after && typeof(this.after) == 'object') {
12624                 this.after = Roo.factory(this.after);
12625                 
12626                 inputblock.cn.push({
12627                     tag :'span',
12628                     cls : 'roo-input-after input-group-append  input-group-' +
12629                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12630                 });
12631             }
12632             
12633             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12634                 inputblock.cls += ' has-feedback';
12635                 inputblock.cn.push(feedback);
12636             }
12637         };
12638         var indicator = {
12639             tag : 'i',
12640             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12641             tooltip : 'This field is required'
12642         };
12643         if (this.allowBlank ) {
12644             indicator.style = this.allowBlank ? ' display:none' : '';
12645         }
12646         if (align ==='left' && this.fieldLabel.length) {
12647             
12648             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12649             
12650             cfg.cn = [
12651                 indicator,
12652                 {
12653                     tag: 'label',
12654                     'for' :  id,
12655                     cls : 'control-label col-form-label',
12656                     html : this.fieldLabel
12657
12658                 },
12659                 {
12660                     cls : "", 
12661                     cn: [
12662                         inputblock
12663                     ]
12664                 }
12665             ];
12666             
12667             var labelCfg = cfg.cn[1];
12668             var contentCfg = cfg.cn[2];
12669             
12670             if(this.indicatorpos == 'right'){
12671                 cfg.cn = [
12672                     {
12673                         tag: 'label',
12674                         'for' :  id,
12675                         cls : 'control-label col-form-label',
12676                         cn : [
12677                             {
12678                                 tag : 'span',
12679                                 html : this.fieldLabel
12680                             },
12681                             indicator
12682                         ]
12683                     },
12684                     {
12685                         cls : "",
12686                         cn: [
12687                             inputblock
12688                         ]
12689                     }
12690
12691                 ];
12692                 
12693                 labelCfg = cfg.cn[0];
12694                 contentCfg = cfg.cn[1];
12695             
12696             }
12697             
12698             if(this.labelWidth > 12){
12699                 labelCfg.style = "width: " + this.labelWidth + 'px';
12700             }
12701             
12702             if(this.labelWidth < 13 && this.labelmd == 0){
12703                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12704             }
12705             
12706             if(this.labellg > 0){
12707                 labelCfg.cls += ' col-lg-' + this.labellg;
12708                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12709             }
12710             
12711             if(this.labelmd > 0){
12712                 labelCfg.cls += ' col-md-' + this.labelmd;
12713                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12714             }
12715             
12716             if(this.labelsm > 0){
12717                 labelCfg.cls += ' col-sm-' + this.labelsm;
12718                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12719             }
12720             
12721             if(this.labelxs > 0){
12722                 labelCfg.cls += ' col-xs-' + this.labelxs;
12723                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12724             }
12725             
12726             
12727         } else if ( this.fieldLabel.length) {
12728                 
12729             
12730             
12731             cfg.cn = [
12732                 {
12733                     tag : 'i',
12734                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12735                     tooltip : 'This field is required',
12736                     style : this.allowBlank ? ' display:none' : '' 
12737                 },
12738                 {
12739                     tag: 'label',
12740                    //cls : 'input-group-addon',
12741                     html : this.fieldLabel
12742
12743                 },
12744
12745                inputblock
12746
12747            ];
12748            
12749            if(this.indicatorpos == 'right'){
12750        
12751                 cfg.cn = [
12752                     {
12753                         tag: 'label',
12754                        //cls : 'input-group-addon',
12755                         html : this.fieldLabel
12756
12757                     },
12758                     {
12759                         tag : 'i',
12760                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12761                         tooltip : 'This field is required',
12762                         style : this.allowBlank ? ' display:none' : '' 
12763                     },
12764
12765                    inputblock
12766
12767                ];
12768
12769             }
12770
12771         } else {
12772             
12773             cfg.cn = [
12774
12775                     inputblock
12776
12777             ];
12778                 
12779                 
12780         };
12781         
12782         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12783            cfg.cls += ' navbar-form';
12784         }
12785         
12786         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12787             // on BS4 we do this only if not form 
12788             cfg.cls += ' navbar-form';
12789             cfg.tag = 'li';
12790         }
12791         
12792         return cfg;
12793         
12794     },
12795     /**
12796      * return the real input element.
12797      */
12798     inputEl: function ()
12799     {
12800         return this.el.select('input.form-control',true).first();
12801     },
12802     
12803     tooltipEl : function()
12804     {
12805         return this.inputEl();
12806     },
12807     
12808     indicatorEl : function()
12809     {
12810         if (Roo.bootstrap.version == 4) {
12811             return false; // not enabled in v4 yet.
12812         }
12813         
12814         var indicator = this.el.select('i.roo-required-indicator',true).first();
12815         
12816         if(!indicator){
12817             return false;
12818         }
12819         
12820         return indicator;
12821         
12822     },
12823     
12824     setDisabled : function(v)
12825     {
12826         var i  = this.inputEl().dom;
12827         if (!v) {
12828             i.removeAttribute('disabled');
12829             return;
12830             
12831         }
12832         i.setAttribute('disabled','true');
12833     },
12834     initEvents : function()
12835     {
12836           
12837         this.inputEl().on("keydown" , this.fireKey,  this);
12838         this.inputEl().on("focus", this.onFocus,  this);
12839         this.inputEl().on("blur", this.onBlur,  this);
12840         
12841         this.inputEl().relayEvent('keyup', this);
12842         this.inputEl().relayEvent('paste', this);
12843         
12844         this.indicator = this.indicatorEl();
12845         
12846         if(this.indicator){
12847             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12848         }
12849  
12850         // reference to original value for reset
12851         this.originalValue = this.getValue();
12852         //Roo.form.TextField.superclass.initEvents.call(this);
12853         if(this.validationEvent == 'keyup'){
12854             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12855             this.inputEl().on('keyup', this.filterValidation, this);
12856         }
12857         else if(this.validationEvent !== false){
12858             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12859         }
12860         
12861         if(this.selectOnFocus){
12862             this.on("focus", this.preFocus, this);
12863             
12864         }
12865         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12866             this.inputEl().on("keypress", this.filterKeys, this);
12867         } else {
12868             this.inputEl().relayEvent('keypress', this);
12869         }
12870        /* if(this.grow){
12871             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12872             this.el.on("click", this.autoSize,  this);
12873         }
12874         */
12875         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12876             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12877         }
12878         
12879         if (typeof(this.before) == 'object') {
12880             this.before.render(this.el.select('.roo-input-before',true).first());
12881         }
12882         if (typeof(this.after) == 'object') {
12883             this.after.render(this.el.select('.roo-input-after',true).first());
12884         }
12885         
12886         this.inputEl().on('change', this.onChange, this);
12887         
12888     },
12889     filterValidation : function(e){
12890         if(!e.isNavKeyPress()){
12891             this.validationTask.delay(this.validationDelay);
12892         }
12893     },
12894      /**
12895      * Validates the field value
12896      * @return {Boolean} True if the value is valid, else false
12897      */
12898     validate : function(){
12899         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12900         if(this.disabled || this.validateValue(this.getRawValue())){
12901             this.markValid();
12902             return true;
12903         }
12904         
12905         this.markInvalid();
12906         return false;
12907     },
12908     
12909     
12910     /**
12911      * Validates a value according to the field's validation rules and marks the field as invalid
12912      * if the validation fails
12913      * @param {Mixed} value The value to validate
12914      * @return {Boolean} True if the value is valid, else false
12915      */
12916     validateValue : function(value)
12917     {
12918         if(this.getVisibilityEl().hasClass('hidden')){
12919             return true;
12920         }
12921         
12922         if(value.length < 1)  { // if it's blank
12923             if(this.allowBlank){
12924                 return true;
12925             }
12926             return false;
12927         }
12928         
12929         if(value.length < this.minLength){
12930             return false;
12931         }
12932         if(value.length > this.maxLength){
12933             return false;
12934         }
12935         if(this.vtype){
12936             var vt = Roo.form.VTypes;
12937             if(!vt[this.vtype](value, this)){
12938                 return false;
12939             }
12940         }
12941         if(typeof this.validator == "function"){
12942             var msg = this.validator(value);
12943             if(msg !== true){
12944                 return false;
12945             }
12946             if (typeof(msg) == 'string') {
12947                 this.invalidText = msg;
12948             }
12949         }
12950         
12951         if(this.regex && !this.regex.test(value)){
12952             return false;
12953         }
12954         
12955         return true;
12956     },
12957     
12958      // private
12959     fireKey : function(e){
12960         //Roo.log('field ' + e.getKey());
12961         if(e.isNavKeyPress()){
12962             this.fireEvent("specialkey", this, e);
12963         }
12964     },
12965     focus : function (selectText){
12966         if(this.rendered){
12967             this.inputEl().focus();
12968             if(selectText === true){
12969                 this.inputEl().dom.select();
12970             }
12971         }
12972         return this;
12973     } ,
12974     
12975     onFocus : function(){
12976         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12977            // this.el.addClass(this.focusClass);
12978         }
12979         if(!this.hasFocus){
12980             this.hasFocus = true;
12981             this.startValue = this.getValue();
12982             this.fireEvent("focus", this);
12983         }
12984     },
12985     
12986     beforeBlur : Roo.emptyFn,
12987
12988     
12989     // private
12990     onBlur : function(){
12991         this.beforeBlur();
12992         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12993             //this.el.removeClass(this.focusClass);
12994         }
12995         this.hasFocus = false;
12996         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12997             this.validate();
12998         }
12999         var v = this.getValue();
13000         if(String(v) !== String(this.startValue)){
13001             this.fireEvent('change', this, v, this.startValue);
13002         }
13003         this.fireEvent("blur", this);
13004     },
13005     
13006     onChange : function(e)
13007     {
13008         var v = this.getValue();
13009         if(String(v) !== String(this.startValue)){
13010             this.fireEvent('change', this, v, this.startValue);
13011         }
13012         
13013     },
13014     
13015     /**
13016      * Resets the current field value to the originally loaded value and clears any validation messages
13017      */
13018     reset : function(){
13019         this.setValue(this.originalValue);
13020         this.validate();
13021     },
13022      /**
13023      * Returns the name of the field
13024      * @return {Mixed} name The name field
13025      */
13026     getName: function(){
13027         return this.name;
13028     },
13029      /**
13030      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13031      * @return {Mixed} value The field value
13032      */
13033     getValue : function(){
13034         
13035         var v = this.inputEl().getValue();
13036         
13037         return v;
13038     },
13039     /**
13040      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13041      * @return {Mixed} value The field value
13042      */
13043     getRawValue : function(){
13044         var v = this.inputEl().getValue();
13045         
13046         return v;
13047     },
13048     
13049     /**
13050      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13051      * @param {Mixed} value The value to set
13052      */
13053     setRawValue : function(v){
13054         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13055     },
13056     
13057     selectText : function(start, end){
13058         var v = this.getRawValue();
13059         if(v.length > 0){
13060             start = start === undefined ? 0 : start;
13061             end = end === undefined ? v.length : end;
13062             var d = this.inputEl().dom;
13063             if(d.setSelectionRange){
13064                 d.setSelectionRange(start, end);
13065             }else if(d.createTextRange){
13066                 var range = d.createTextRange();
13067                 range.moveStart("character", start);
13068                 range.moveEnd("character", v.length-end);
13069                 range.select();
13070             }
13071         }
13072     },
13073     
13074     /**
13075      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13076      * @param {Mixed} value The value to set
13077      */
13078     setValue : function(v){
13079         this.value = v;
13080         if(this.rendered){
13081             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13082             this.validate();
13083         }
13084     },
13085     
13086     /*
13087     processValue : function(value){
13088         if(this.stripCharsRe){
13089             var newValue = value.replace(this.stripCharsRe, '');
13090             if(newValue !== value){
13091                 this.setRawValue(newValue);
13092                 return newValue;
13093             }
13094         }
13095         return value;
13096     },
13097   */
13098     preFocus : function(){
13099         
13100         if(this.selectOnFocus){
13101             this.inputEl().dom.select();
13102         }
13103     },
13104     filterKeys : function(e){
13105         var k = e.getKey();
13106         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13107             return;
13108         }
13109         var c = e.getCharCode(), cc = String.fromCharCode(c);
13110         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13111             return;
13112         }
13113         if(!this.maskRe.test(cc)){
13114             e.stopEvent();
13115         }
13116     },
13117      /**
13118      * Clear any invalid styles/messages for this field
13119      */
13120     clearInvalid : function(){
13121         
13122         if(!this.el || this.preventMark){ // not rendered
13123             return;
13124         }
13125         
13126         
13127         this.el.removeClass([this.invalidClass, 'is-invalid']);
13128         
13129         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13130             
13131             var feedback = this.el.select('.form-control-feedback', true).first();
13132             
13133             if(feedback){
13134                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13135             }
13136             
13137         }
13138         
13139         if(this.indicator){
13140             this.indicator.removeClass('visible');
13141             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13142         }
13143         
13144         this.fireEvent('valid', this);
13145     },
13146     
13147      /**
13148      * Mark this field as valid
13149      */
13150     markValid : function()
13151     {
13152         if(!this.el  || this.preventMark){ // not rendered...
13153             return;
13154         }
13155         
13156         this.el.removeClass([this.invalidClass, this.validClass]);
13157         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13158
13159         var feedback = this.el.select('.form-control-feedback', true).first();
13160             
13161         if(feedback){
13162             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13163         }
13164         
13165         if(this.indicator){
13166             this.indicator.removeClass('visible');
13167             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13168         }
13169         
13170         if(this.disabled){
13171             return;
13172         }
13173         
13174            
13175         if(this.allowBlank && !this.getRawValue().length){
13176             return;
13177         }
13178         if (Roo.bootstrap.version == 3) {
13179             this.el.addClass(this.validClass);
13180         } else {
13181             this.inputEl().addClass('is-valid');
13182         }
13183
13184         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13185             
13186             var feedback = this.el.select('.form-control-feedback', true).first();
13187             
13188             if(feedback){
13189                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13190                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13191             }
13192             
13193         }
13194         
13195         this.fireEvent('valid', this);
13196     },
13197     
13198      /**
13199      * Mark this field as invalid
13200      * @param {String} msg The validation message
13201      */
13202     markInvalid : function(msg)
13203     {
13204         if(!this.el  || this.preventMark){ // not rendered
13205             return;
13206         }
13207         
13208         this.el.removeClass([this.invalidClass, this.validClass]);
13209         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13210         
13211         var feedback = this.el.select('.form-control-feedback', true).first();
13212             
13213         if(feedback){
13214             this.el.select('.form-control-feedback', true).first().removeClass(
13215                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13216         }
13217
13218         if(this.disabled){
13219             return;
13220         }
13221         
13222         if(this.allowBlank && !this.getRawValue().length){
13223             return;
13224         }
13225         
13226         if(this.indicator){
13227             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13228             this.indicator.addClass('visible');
13229         }
13230         if (Roo.bootstrap.version == 3) {
13231             this.el.addClass(this.invalidClass);
13232         } else {
13233             this.inputEl().addClass('is-invalid');
13234         }
13235         
13236         
13237         
13238         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13239             
13240             var feedback = this.el.select('.form-control-feedback', true).first();
13241             
13242             if(feedback){
13243                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13244                 
13245                 if(this.getValue().length || this.forceFeedback){
13246                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13247                 }
13248                 
13249             }
13250             
13251         }
13252         
13253         this.fireEvent('invalid', this, msg);
13254     },
13255     // private
13256     SafariOnKeyDown : function(event)
13257     {
13258         // this is a workaround for a password hang bug on chrome/ webkit.
13259         if (this.inputEl().dom.type != 'password') {
13260             return;
13261         }
13262         
13263         var isSelectAll = false;
13264         
13265         if(this.inputEl().dom.selectionEnd > 0){
13266             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13267         }
13268         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13269             event.preventDefault();
13270             this.setValue('');
13271             return;
13272         }
13273         
13274         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13275             
13276             event.preventDefault();
13277             // this is very hacky as keydown always get's upper case.
13278             //
13279             var cc = String.fromCharCode(event.getCharCode());
13280             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13281             
13282         }
13283     },
13284     adjustWidth : function(tag, w){
13285         tag = tag.toLowerCase();
13286         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13287             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13288                 if(tag == 'input'){
13289                     return w + 2;
13290                 }
13291                 if(tag == 'textarea'){
13292                     return w-2;
13293                 }
13294             }else if(Roo.isOpera){
13295                 if(tag == 'input'){
13296                     return w + 2;
13297                 }
13298                 if(tag == 'textarea'){
13299                     return w-2;
13300                 }
13301             }
13302         }
13303         return w;
13304     },
13305     
13306     setFieldLabel : function(v)
13307     {
13308         if(!this.rendered){
13309             return;
13310         }
13311         
13312         if(this.indicatorEl()){
13313             var ar = this.el.select('label > span',true);
13314             
13315             if (ar.elements.length) {
13316                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13317                 this.fieldLabel = v;
13318                 return;
13319             }
13320             
13321             var br = this.el.select('label',true);
13322             
13323             if(br.elements.length) {
13324                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13325                 this.fieldLabel = v;
13326                 return;
13327             }
13328             
13329             Roo.log('Cannot Found any of label > span || label in input');
13330             return;
13331         }
13332         
13333         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13334         this.fieldLabel = v;
13335         
13336         
13337     }
13338 });
13339
13340  
13341 /*
13342  * - LGPL
13343  *
13344  * Input
13345  * 
13346  */
13347
13348 /**
13349  * @class Roo.bootstrap.form.TextArea
13350  * @extends Roo.bootstrap.form.Input
13351  * Bootstrap TextArea class
13352  * @cfg {Number} cols Specifies the visible width of a text area
13353  * @cfg {Number} rows Specifies the visible number of lines in a text area
13354  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13355  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13356  * @cfg {string} html text
13357  * 
13358  * @constructor
13359  * Create a new TextArea
13360  * @param {Object} config The config object
13361  */
13362
13363 Roo.bootstrap.form.TextArea = function(config){
13364     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13365    
13366 };
13367
13368 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13369      
13370     cols : false,
13371     rows : 5,
13372     readOnly : false,
13373     warp : 'soft',
13374     resize : false,
13375     value: false,
13376     html: false,
13377     
13378     getAutoCreate : function(){
13379         
13380         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13381         
13382         var id = Roo.id();
13383         
13384         var cfg = {};
13385         
13386         if(this.inputType != 'hidden'){
13387             cfg.cls = 'form-group' //input-group
13388         }
13389         
13390         var input =  {
13391             tag: 'textarea',
13392             id : id,
13393             warp : this.warp,
13394             rows : this.rows,
13395             value : this.value || '',
13396             html: this.html || '',
13397             cls : 'form-control',
13398             placeholder : this.placeholder || '' 
13399             
13400         };
13401         
13402         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13403             input.maxLength = this.maxLength;
13404         }
13405         
13406         if(this.resize){
13407             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13408         }
13409         
13410         if(this.cols){
13411             input.cols = this.cols;
13412         }
13413         
13414         if (this.readOnly) {
13415             input.readonly = true;
13416         }
13417         
13418         if (this.name) {
13419             input.name = this.name;
13420         }
13421         
13422         if (this.size) {
13423             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13424         }
13425         
13426         var settings=this;
13427         ['xs','sm','md','lg'].map(function(size){
13428             if (settings[size]) {
13429                 cfg.cls += ' col-' + size + '-' + settings[size];
13430             }
13431         });
13432         
13433         var inputblock = input;
13434         
13435         if(this.hasFeedback && !this.allowBlank){
13436             
13437             var feedback = {
13438                 tag: 'span',
13439                 cls: 'glyphicon form-control-feedback'
13440             };
13441
13442             inputblock = {
13443                 cls : 'has-feedback',
13444                 cn :  [
13445                     input,
13446                     feedback
13447                 ] 
13448             };  
13449         }
13450         
13451         
13452         if (this.before || this.after) {
13453             
13454             inputblock = {
13455                 cls : 'input-group',
13456                 cn :  [] 
13457             };
13458             if (this.before) {
13459                 inputblock.cn.push({
13460                     tag :'span',
13461                     cls : 'input-group-addon',
13462                     html : this.before
13463                 });
13464             }
13465             
13466             inputblock.cn.push(input);
13467             
13468             if(this.hasFeedback && !this.allowBlank){
13469                 inputblock.cls += ' has-feedback';
13470                 inputblock.cn.push(feedback);
13471             }
13472             
13473             if (this.after) {
13474                 inputblock.cn.push({
13475                     tag :'span',
13476                     cls : 'input-group-addon',
13477                     html : this.after
13478                 });
13479             }
13480             
13481         }
13482         
13483         if (align ==='left' && this.fieldLabel.length) {
13484             cfg.cn = [
13485                 {
13486                     tag: 'label',
13487                     'for' :  id,
13488                     cls : 'control-label',
13489                     html : this.fieldLabel
13490                 },
13491                 {
13492                     cls : "",
13493                     cn: [
13494                         inputblock
13495                     ]
13496                 }
13497
13498             ];
13499             
13500             if(this.labelWidth > 12){
13501                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13502             }
13503
13504             if(this.labelWidth < 13 && this.labelmd == 0){
13505                 this.labelmd = this.labelWidth;
13506             }
13507
13508             if(this.labellg > 0){
13509                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13510                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13511             }
13512
13513             if(this.labelmd > 0){
13514                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13515                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13516             }
13517
13518             if(this.labelsm > 0){
13519                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13520                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13521             }
13522
13523             if(this.labelxs > 0){
13524                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13525                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13526             }
13527             
13528         } else if ( this.fieldLabel.length) {
13529             cfg.cn = [
13530
13531                {
13532                    tag: 'label',
13533                    //cls : 'input-group-addon',
13534                    html : this.fieldLabel
13535
13536                },
13537
13538                inputblock
13539
13540            ];
13541
13542         } else {
13543
13544             cfg.cn = [
13545
13546                 inputblock
13547
13548             ];
13549                 
13550         }
13551         
13552         if (this.disabled) {
13553             input.disabled=true;
13554         }
13555         
13556         return cfg;
13557         
13558     },
13559     /**
13560      * return the real textarea element.
13561      */
13562     inputEl: function ()
13563     {
13564         return this.el.select('textarea.form-control',true).first();
13565     },
13566     
13567     /**
13568      * Clear any invalid styles/messages for this field
13569      */
13570     clearInvalid : function()
13571     {
13572         
13573         if(!this.el || this.preventMark){ // not rendered
13574             return;
13575         }
13576         
13577         var label = this.el.select('label', true).first();
13578         var icon = this.el.select('i.fa-star', true).first();
13579         
13580         if(label && icon){
13581             icon.remove();
13582         }
13583         this.el.removeClass( this.validClass);
13584         this.inputEl().removeClass('is-invalid');
13585          
13586         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13587             
13588             var feedback = this.el.select('.form-control-feedback', true).first();
13589             
13590             if(feedback){
13591                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13592             }
13593             
13594         }
13595         
13596         this.fireEvent('valid', this);
13597     },
13598     
13599      /**
13600      * Mark this field as valid
13601      */
13602     markValid : function()
13603     {
13604         if(!this.el  || this.preventMark){ // not rendered
13605             return;
13606         }
13607         
13608         this.el.removeClass([this.invalidClass, this.validClass]);
13609         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13610         
13611         var feedback = this.el.select('.form-control-feedback', true).first();
13612             
13613         if(feedback){
13614             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13615         }
13616
13617         if(this.disabled || this.allowBlank){
13618             return;
13619         }
13620         
13621         var label = this.el.select('label', true).first();
13622         var icon = this.el.select('i.fa-star', true).first();
13623         
13624         if(label && icon){
13625             icon.remove();
13626         }
13627         if (Roo.bootstrap.version == 3) {
13628             this.el.addClass(this.validClass);
13629         } else {
13630             this.inputEl().addClass('is-valid');
13631         }
13632         
13633         
13634         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13635             
13636             var feedback = this.el.select('.form-control-feedback', true).first();
13637             
13638             if(feedback){
13639                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13640                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13641             }
13642             
13643         }
13644         
13645         this.fireEvent('valid', this);
13646     },
13647     
13648      /**
13649      * Mark this field as invalid
13650      * @param {String} msg The validation message
13651      */
13652     markInvalid : function(msg)
13653     {
13654         if(!this.el  || this.preventMark){ // not rendered
13655             return;
13656         }
13657         
13658         this.el.removeClass([this.invalidClass, this.validClass]);
13659         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13660         
13661         var feedback = this.el.select('.form-control-feedback', true).first();
13662             
13663         if(feedback){
13664             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13665         }
13666
13667         if(this.disabled || this.allowBlank){
13668             return;
13669         }
13670         
13671         var label = this.el.select('label', true).first();
13672         var icon = this.el.select('i.fa-star', true).first();
13673         
13674         if(!this.getValue().length && label && !icon){
13675             this.el.createChild({
13676                 tag : 'i',
13677                 cls : 'text-danger fa fa-lg fa-star',
13678                 tooltip : 'This field is required',
13679                 style : 'margin-right:5px;'
13680             }, label, true);
13681         }
13682         
13683         if (Roo.bootstrap.version == 3) {
13684             this.el.addClass(this.invalidClass);
13685         } else {
13686             this.inputEl().addClass('is-invalid');
13687         }
13688         
13689         // fixme ... this may be depricated need to test..
13690         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13691             
13692             var feedback = this.el.select('.form-control-feedback', true).first();
13693             
13694             if(feedback){
13695                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13696                 
13697                 if(this.getValue().length || this.forceFeedback){
13698                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13699                 }
13700                 
13701             }
13702             
13703         }
13704         
13705         this.fireEvent('invalid', this, msg);
13706     }
13707 });
13708
13709  
13710 /*
13711  * - LGPL
13712  *
13713  * trigger field - base class for combo..
13714  * 
13715  */
13716  
13717 /**
13718  * @class Roo.bootstrap.form.TriggerField
13719  * @extends Roo.bootstrap.form.Input
13720  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13721  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13722  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13723  * for which you can provide a custom implementation.  For example:
13724  * <pre><code>
13725 var trigger = new Roo.bootstrap.form.TriggerField();
13726 trigger.onTriggerClick = myTriggerFn;
13727 trigger.applyTo('my-field');
13728 </code></pre>
13729  *
13730  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13731  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13732  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13733  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13734  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13735
13736  * @constructor
13737  * Create a new TriggerField.
13738  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13739  * to the base TextField)
13740  */
13741 Roo.bootstrap.form.TriggerField = function(config){
13742     this.mimicing = false;
13743     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13744 };
13745
13746 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13747     /**
13748      * @cfg {String} triggerClass A CSS class to apply to the trigger
13749      */
13750      /**
13751      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13752      */
13753     hideTrigger:false,
13754
13755     /**
13756      * @cfg {Boolean} removable (true|false) special filter default false
13757      */
13758     removable : false,
13759     
13760     /** @cfg {Boolean} grow @hide */
13761     /** @cfg {Number} growMin @hide */
13762     /** @cfg {Number} growMax @hide */
13763
13764     /**
13765      * @hide 
13766      * @method
13767      */
13768     autoSize: Roo.emptyFn,
13769     // private
13770     monitorTab : true,
13771     // private
13772     deferHeight : true,
13773
13774     
13775     actionMode : 'wrap',
13776     
13777     caret : false,
13778     
13779     
13780     getAutoCreate : function(){
13781        
13782         var align = this.labelAlign || this.parentLabelAlign();
13783         
13784         var id = Roo.id();
13785         
13786         var cfg = {
13787             cls: 'form-group' //input-group
13788         };
13789         
13790         
13791         var input =  {
13792             tag: 'input',
13793             id : id,
13794             type : this.inputType,
13795             cls : 'form-control',
13796             autocomplete: 'new-password',
13797             placeholder : this.placeholder || '' 
13798             
13799         };
13800         if (this.name) {
13801             input.name = this.name;
13802         }
13803         if (this.size) {
13804             input.cls += ' input-' + this.size;
13805         }
13806         
13807         if (this.disabled) {
13808             input.disabled=true;
13809         }
13810         
13811         var inputblock = input;
13812         
13813         if(this.hasFeedback && !this.allowBlank){
13814             
13815             var feedback = {
13816                 tag: 'span',
13817                 cls: 'glyphicon form-control-feedback'
13818             };
13819             
13820             if(this.removable && !this.editable  ){
13821                 inputblock = {
13822                     cls : 'has-feedback',
13823                     cn :  [
13824                         inputblock,
13825                         {
13826                             tag: 'button',
13827                             html : 'x',
13828                             cls : 'roo-combo-removable-btn close'
13829                         },
13830                         feedback
13831                     ] 
13832                 };
13833             } else {
13834                 inputblock = {
13835                     cls : 'has-feedback',
13836                     cn :  [
13837                         inputblock,
13838                         feedback
13839                     ] 
13840                 };
13841             }
13842
13843         } else {
13844             if(this.removable && !this.editable ){
13845                 inputblock = {
13846                     cls : 'roo-removable',
13847                     cn :  [
13848                         inputblock,
13849                         {
13850                             tag: 'button',
13851                             html : 'x',
13852                             cls : 'roo-combo-removable-btn close'
13853                         }
13854                     ] 
13855                 };
13856             }
13857         }
13858         
13859         if (this.before || this.after) {
13860             
13861             inputblock = {
13862                 cls : 'input-group',
13863                 cn :  [] 
13864             };
13865             if (this.before) {
13866                 inputblock.cn.push({
13867                     tag :'span',
13868                     cls : 'input-group-addon input-group-prepend input-group-text',
13869                     html : this.before
13870                 });
13871             }
13872             
13873             inputblock.cn.push(input);
13874             
13875             if(this.hasFeedback && !this.allowBlank){
13876                 inputblock.cls += ' has-feedback';
13877                 inputblock.cn.push(feedback);
13878             }
13879             
13880             if (this.after) {
13881                 inputblock.cn.push({
13882                     tag :'span',
13883                     cls : 'input-group-addon input-group-append input-group-text',
13884                     html : this.after
13885                 });
13886             }
13887             
13888         };
13889         
13890       
13891         
13892         var ibwrap = inputblock;
13893         
13894         if(this.multiple){
13895             ibwrap = {
13896                 tag: 'ul',
13897                 cls: 'roo-select2-choices',
13898                 cn:[
13899                     {
13900                         tag: 'li',
13901                         cls: 'roo-select2-search-field',
13902                         cn: [
13903
13904                             inputblock
13905                         ]
13906                     }
13907                 ]
13908             };
13909                 
13910         }
13911         
13912         var combobox = {
13913             cls: 'roo-select2-container input-group',
13914             cn: [
13915                  {
13916                     tag: 'input',
13917                     type : 'hidden',
13918                     cls: 'form-hidden-field'
13919                 },
13920                 ibwrap
13921             ]
13922         };
13923         
13924         if(!this.multiple && this.showToggleBtn){
13925             
13926             var caret = {
13927                         tag: 'span',
13928                         cls: 'caret'
13929              };
13930             if (this.caret != false) {
13931                 caret = {
13932                      tag: 'i',
13933                      cls: 'fa fa-' + this.caret
13934                 };
13935                 
13936             }
13937             
13938             combobox.cn.push({
13939                 tag :'span',
13940                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13941                 cn : [
13942                     Roo.bootstrap.version == 3 ? caret : '',
13943                     {
13944                         tag: 'span',
13945                         cls: 'combobox-clear',
13946                         cn  : [
13947                             {
13948                                 tag : 'i',
13949                                 cls: 'icon-remove'
13950                             }
13951                         ]
13952                     }
13953                 ]
13954
13955             })
13956         }
13957         
13958         if(this.multiple){
13959             combobox.cls += ' roo-select2-container-multi';
13960         }
13961          var indicator = {
13962             tag : 'i',
13963             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13964             tooltip : 'This field is required'
13965         };
13966         if (Roo.bootstrap.version == 4) {
13967             indicator = {
13968                 tag : 'i',
13969                 style : 'display:none'
13970             };
13971         }
13972         
13973         
13974         if (align ==='left' && this.fieldLabel.length) {
13975             
13976             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13977
13978             cfg.cn = [
13979                 indicator,
13980                 {
13981                     tag: 'label',
13982                     'for' :  id,
13983                     cls : 'control-label',
13984                     html : this.fieldLabel
13985
13986                 },
13987                 {
13988                     cls : "", 
13989                     cn: [
13990                         combobox
13991                     ]
13992                 }
13993
13994             ];
13995             
13996             var labelCfg = cfg.cn[1];
13997             var contentCfg = cfg.cn[2];
13998             
13999             if(this.indicatorpos == 'right'){
14000                 cfg.cn = [
14001                     {
14002                         tag: 'label',
14003                         'for' :  id,
14004                         cls : 'control-label',
14005                         cn : [
14006                             {
14007                                 tag : 'span',
14008                                 html : this.fieldLabel
14009                             },
14010                             indicator
14011                         ]
14012                     },
14013                     {
14014                         cls : "", 
14015                         cn: [
14016                             combobox
14017                         ]
14018                     }
14019
14020                 ];
14021                 
14022                 labelCfg = cfg.cn[0];
14023                 contentCfg = cfg.cn[1];
14024             }
14025             
14026             if(this.labelWidth > 12){
14027                 labelCfg.style = "width: " + this.labelWidth + 'px';
14028             }
14029             
14030             if(this.labelWidth < 13 && this.labelmd == 0){
14031                 this.labelmd = this.labelWidth;
14032             }
14033             
14034             if(this.labellg > 0){
14035                 labelCfg.cls += ' col-lg-' + this.labellg;
14036                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14037             }
14038             
14039             if(this.labelmd > 0){
14040                 labelCfg.cls += ' col-md-' + this.labelmd;
14041                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14042             }
14043             
14044             if(this.labelsm > 0){
14045                 labelCfg.cls += ' col-sm-' + this.labelsm;
14046                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14047             }
14048             
14049             if(this.labelxs > 0){
14050                 labelCfg.cls += ' col-xs-' + this.labelxs;
14051                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14052             }
14053             
14054         } else if ( this.fieldLabel.length) {
14055 //                Roo.log(" label");
14056             cfg.cn = [
14057                 indicator,
14058                {
14059                    tag: 'label',
14060                    //cls : 'input-group-addon',
14061                    html : this.fieldLabel
14062
14063                },
14064
14065                combobox
14066
14067             ];
14068             
14069             if(this.indicatorpos == 'right'){
14070                 
14071                 cfg.cn = [
14072                     {
14073                        tag: 'label',
14074                        cn : [
14075                            {
14076                                tag : 'span',
14077                                html : this.fieldLabel
14078                            },
14079                            indicator
14080                        ]
14081
14082                     },
14083                     combobox
14084
14085                 ];
14086
14087             }
14088
14089         } else {
14090             
14091 //                Roo.log(" no label && no align");
14092                 cfg = combobox
14093                      
14094                 
14095         }
14096         
14097         var settings=this;
14098         ['xs','sm','md','lg'].map(function(size){
14099             if (settings[size]) {
14100                 cfg.cls += ' col-' + size + '-' + settings[size];
14101             }
14102         });
14103         
14104         return cfg;
14105         
14106     },
14107     
14108     
14109     
14110     // private
14111     onResize : function(w, h){
14112 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14113 //        if(typeof w == 'number'){
14114 //            var x = w - this.trigger.getWidth();
14115 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14116 //            this.trigger.setStyle('left', x+'px');
14117 //        }
14118     },
14119
14120     // private
14121     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14122
14123     // private
14124     getResizeEl : function(){
14125         return this.inputEl();
14126     },
14127
14128     // private
14129     getPositionEl : function(){
14130         return this.inputEl();
14131     },
14132
14133     // private
14134     alignErrorIcon : function(){
14135         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14136     },
14137
14138     // private
14139     initEvents : function(){
14140         
14141         this.createList();
14142         
14143         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14144         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14145         if(!this.multiple && this.showToggleBtn){
14146             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14147             if(this.hideTrigger){
14148                 this.trigger.setDisplayed(false);
14149             }
14150             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14151         }
14152         
14153         if(this.multiple){
14154             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14155         }
14156         
14157         if(this.removable && !this.editable && !this.tickable){
14158             var close = this.closeTriggerEl();
14159             
14160             if(close){
14161                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14162                 close.on('click', this.removeBtnClick, this, close);
14163             }
14164         }
14165         
14166         //this.trigger.addClassOnOver('x-form-trigger-over');
14167         //this.trigger.addClassOnClick('x-form-trigger-click');
14168         
14169         //if(!this.width){
14170         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14171         //}
14172     },
14173     
14174     closeTriggerEl : function()
14175     {
14176         var close = this.el.select('.roo-combo-removable-btn', true).first();
14177         return close ? close : false;
14178     },
14179     
14180     removeBtnClick : function(e, h, el)
14181     {
14182         e.preventDefault();
14183         
14184         if(this.fireEvent("remove", this) !== false){
14185             this.reset();
14186             this.fireEvent("afterremove", this)
14187         }
14188     },
14189     
14190     createList : function()
14191     {
14192         this.list = Roo.get(document.body).createChild({
14193             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14194             cls: 'typeahead typeahead-long dropdown-menu shadow',
14195             style: 'display:none'
14196         });
14197         
14198         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14199         
14200     },
14201
14202     // private
14203     initTrigger : function(){
14204        
14205     },
14206
14207     // private
14208     onDestroy : function(){
14209         if(this.trigger){
14210             this.trigger.removeAllListeners();
14211           //  this.trigger.remove();
14212         }
14213         //if(this.wrap){
14214         //    this.wrap.remove();
14215         //}
14216         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14217     },
14218
14219     // private
14220     onFocus : function(){
14221         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14222         /*
14223         if(!this.mimicing){
14224             this.wrap.addClass('x-trigger-wrap-focus');
14225             this.mimicing = true;
14226             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14227             if(this.monitorTab){
14228                 this.el.on("keydown", this.checkTab, this);
14229             }
14230         }
14231         */
14232     },
14233
14234     // private
14235     checkTab : function(e){
14236         if(e.getKey() == e.TAB){
14237             this.triggerBlur();
14238         }
14239     },
14240
14241     // private
14242     onBlur : function(){
14243         // do nothing
14244     },
14245
14246     // private
14247     mimicBlur : function(e, t){
14248         /*
14249         if(!this.wrap.contains(t) && this.validateBlur()){
14250             this.triggerBlur();
14251         }
14252         */
14253     },
14254
14255     // private
14256     triggerBlur : function(){
14257         this.mimicing = false;
14258         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14259         if(this.monitorTab){
14260             this.el.un("keydown", this.checkTab, this);
14261         }
14262         //this.wrap.removeClass('x-trigger-wrap-focus');
14263         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14264     },
14265
14266     // private
14267     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14268     validateBlur : function(e, t){
14269         return true;
14270     },
14271
14272     // private
14273     onDisable : function(){
14274         this.inputEl().dom.disabled = true;
14275         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14276         //if(this.wrap){
14277         //    this.wrap.addClass('x-item-disabled');
14278         //}
14279     },
14280
14281     // private
14282     onEnable : function(){
14283         this.inputEl().dom.disabled = false;
14284         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14285         //if(this.wrap){
14286         //    this.el.removeClass('x-item-disabled');
14287         //}
14288     },
14289
14290     // private
14291     onShow : function(){
14292         var ae = this.getActionEl();
14293         
14294         if(ae){
14295             ae.dom.style.display = '';
14296             ae.dom.style.visibility = 'visible';
14297         }
14298     },
14299
14300     // private
14301     
14302     onHide : function(){
14303         var ae = this.getActionEl();
14304         ae.dom.style.display = 'none';
14305     },
14306
14307     /**
14308      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14309      * by an implementing function.
14310      * @method
14311      * @param {EventObject} e
14312      */
14313     onTriggerClick : Roo.emptyFn
14314 });
14315  
14316 /*
14317 * Licence: LGPL
14318 */
14319
14320 /**
14321  * @class Roo.bootstrap.form.CardUploader
14322  * @extends Roo.bootstrap.Button
14323  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14324  * @cfg {Number} errorTimeout default 3000
14325  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14326  * @cfg {Array}  html The button text.
14327
14328  *
14329  * @constructor
14330  * Create a new CardUploader
14331  * @param {Object} config The config object
14332  */
14333
14334 Roo.bootstrap.form.CardUploader = function(config){
14335     
14336  
14337     
14338     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14339     
14340     
14341     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14342         return r.data.id
14343      });
14344     
14345      this.addEvents({
14346          // raw events
14347         /**
14348          * @event preview
14349          * When a image is clicked on - and needs to display a slideshow or similar..
14350          * @param {Roo.bootstrap.Card} this
14351          * @param {Object} The image information data 
14352          *
14353          */
14354         'preview' : true,
14355          /**
14356          * @event download
14357          * When a the download link is clicked
14358          * @param {Roo.bootstrap.Card} this
14359          * @param {Object} The image information data  contains 
14360          */
14361         'download' : true
14362         
14363     });
14364 };
14365  
14366 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14367     
14368      
14369     errorTimeout : 3000,
14370      
14371     images : false,
14372    
14373     fileCollection : false,
14374     allowBlank : true,
14375     
14376     getAutoCreate : function()
14377     {
14378         
14379         var cfg =  {
14380             cls :'form-group' ,
14381             cn : [
14382                
14383                 {
14384                     tag: 'label',
14385                    //cls : 'input-group-addon',
14386                     html : this.fieldLabel
14387
14388                 },
14389
14390                 {
14391                     tag: 'input',
14392                     type : 'hidden',
14393                     name : this.name,
14394                     value : this.value,
14395                     cls : 'd-none  form-control'
14396                 },
14397                 
14398                 {
14399                     tag: 'input',
14400                     multiple : 'multiple',
14401                     type : 'file',
14402                     cls : 'd-none  roo-card-upload-selector'
14403                 },
14404                 
14405                 {
14406                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14407                 },
14408                 {
14409                     cls : 'card-columns roo-card-uploader-container'
14410                 }
14411
14412             ]
14413         };
14414            
14415          
14416         return cfg;
14417     },
14418     
14419     getChildContainer : function() /// what children are added to.
14420     {
14421         return this.containerEl;
14422     },
14423    
14424     getButtonContainer : function() /// what children are added to.
14425     {
14426         return this.el.select(".roo-card-uploader-button-container").first();
14427     },
14428    
14429     initEvents : function()
14430     {
14431         
14432         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14433         
14434         var t = this;
14435         this.addxtype({
14436             xns: Roo.bootstrap,
14437
14438             xtype : 'Button',
14439             container_method : 'getButtonContainer' ,            
14440             html :  this.html, // fix changable?
14441             cls : 'w-100 ',
14442             listeners : {
14443                 'click' : function(btn, e) {
14444                     t.onClick(e);
14445                 }
14446             }
14447         });
14448         
14449         
14450         
14451         
14452         this.urlAPI = (window.createObjectURL && window) || 
14453                                 (window.URL && URL.revokeObjectURL && URL) || 
14454                                 (window.webkitURL && webkitURL);
14455                         
14456          
14457          
14458          
14459         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14460         
14461         this.selectorEl.on('change', this.onFileSelected, this);
14462         if (this.images) {
14463             var t = this;
14464             this.images.forEach(function(img) {
14465                 t.addCard(img)
14466             });
14467             this.images = false;
14468         }
14469         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14470          
14471        
14472     },
14473     
14474    
14475     onClick : function(e)
14476     {
14477         e.preventDefault();
14478          
14479         this.selectorEl.dom.click();
14480          
14481     },
14482     
14483     onFileSelected : function(e)
14484     {
14485         e.preventDefault();
14486         
14487         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14488             return;
14489         }
14490         
14491         Roo.each(this.selectorEl.dom.files, function(file){    
14492             this.addFile(file);
14493         }, this);
14494          
14495     },
14496     
14497       
14498     
14499       
14500     
14501     addFile : function(file)
14502     {
14503            
14504         if(typeof(file) === 'string'){
14505             throw "Add file by name?"; // should not happen
14506             return;
14507         }
14508         
14509         if(!file || !this.urlAPI){
14510             return;
14511         }
14512         
14513         // file;
14514         // file.type;
14515         
14516         var _this = this;
14517         
14518         
14519         var url = _this.urlAPI.createObjectURL( file);
14520            
14521         this.addCard({
14522             id : Roo.bootstrap.form.CardUploader.ID--,
14523             is_uploaded : false,
14524             src : url,
14525             srcfile : file,
14526             title : file.name,
14527             mimetype : file.type,
14528             preview : false,
14529             is_deleted : 0
14530         });
14531         
14532     },
14533     
14534     /**
14535      * addCard - add an Attachment to the uploader
14536      * @param data - the data about the image to upload
14537      *
14538      * {
14539           id : 123
14540           title : "Title of file",
14541           is_uploaded : false,
14542           src : "http://.....",
14543           srcfile : { the File upload object },
14544           mimetype : file.type,
14545           preview : false,
14546           is_deleted : 0
14547           .. any other data...
14548         }
14549      *
14550      * 
14551     */
14552     
14553     addCard : function (data)
14554     {
14555         // hidden input element?
14556         // if the file is not an image...
14557         //then we need to use something other that and header_image
14558         var t = this;
14559         //   remove.....
14560         var footer = [
14561             {
14562                 xns : Roo.bootstrap,
14563                 xtype : 'CardFooter',
14564                  items: [
14565                     {
14566                         xns : Roo.bootstrap,
14567                         xtype : 'Element',
14568                         cls : 'd-flex',
14569                         items : [
14570                             
14571                             {
14572                                 xns : Roo.bootstrap,
14573                                 xtype : 'Button',
14574                                 html : String.format("<small>{0}</small>", data.title),
14575                                 cls : 'col-10 text-left',
14576                                 size: 'sm',
14577                                 weight: 'link',
14578                                 fa : 'download',
14579                                 listeners : {
14580                                     click : function() {
14581                                      
14582                                         t.fireEvent( "download", t, data );
14583                                     }
14584                                 }
14585                             },
14586                           
14587                             {
14588                                 xns : Roo.bootstrap,
14589                                 xtype : 'Button',
14590                                 style: 'max-height: 28px; ',
14591                                 size : 'sm',
14592                                 weight: 'danger',
14593                                 cls : 'col-2',
14594                                 fa : 'times',
14595                                 listeners : {
14596                                     click : function() {
14597                                         t.removeCard(data.id)
14598                                     }
14599                                 }
14600                             }
14601                         ]
14602                     }
14603                     
14604                 ] 
14605             }
14606             
14607         ];
14608         
14609         var cn = this.addxtype(
14610             {
14611                  
14612                 xns : Roo.bootstrap,
14613                 xtype : 'Card',
14614                 closeable : true,
14615                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14616                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14617                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14618                 data : data,
14619                 html : false,
14620                  
14621                 items : footer,
14622                 initEvents : function() {
14623                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14624                     var card = this;
14625                     this.imgEl = this.el.select('.card-img-top').first();
14626                     if (this.imgEl) {
14627                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14628                         this.imgEl.set({ 'pointer' : 'cursor' });
14629                                   
14630                     }
14631                     this.getCardFooter().addClass('p-1');
14632                     
14633                   
14634                 }
14635                 
14636             }
14637         );
14638         // dont' really need ot update items.
14639         // this.items.push(cn);
14640         this.fileCollection.add(cn);
14641         
14642         if (!data.srcfile) {
14643             this.updateInput();
14644             return;
14645         }
14646             
14647         var _t = this;
14648         var reader = new FileReader();
14649         reader.addEventListener("load", function() {  
14650             data.srcdata =  reader.result;
14651             _t.updateInput();
14652         });
14653         reader.readAsDataURL(data.srcfile);
14654         
14655         
14656         
14657     },
14658     removeCard : function(id)
14659     {
14660         
14661         var card  = this.fileCollection.get(id);
14662         card.data.is_deleted = 1;
14663         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14664         //this.fileCollection.remove(card);
14665         //this.items = this.items.filter(function(e) { return e != card });
14666         // dont' really need ot update items.
14667         card.el.dom.parentNode.removeChild(card.el.dom);
14668         this.updateInput();
14669
14670         
14671     },
14672     reset: function()
14673     {
14674         this.fileCollection.each(function(card) {
14675             if (card.el.dom && card.el.dom.parentNode) {
14676                 card.el.dom.parentNode.removeChild(card.el.dom);
14677             }
14678         });
14679         this.fileCollection.clear();
14680         this.updateInput();
14681     },
14682     
14683     updateInput : function()
14684     {
14685          var data = [];
14686         this.fileCollection.each(function(e) {
14687             data.push(e.data);
14688             
14689         });
14690         this.inputEl().dom.value = JSON.stringify(data);
14691         
14692         
14693         
14694     }
14695     
14696     
14697 });
14698
14699
14700 Roo.bootstrap.form.CardUploader.ID = -1;/*
14701  * Based on:
14702  * Ext JS Library 1.1.1
14703  * Copyright(c) 2006-2007, Ext JS, LLC.
14704  *
14705  * Originally Released Under LGPL - original licence link has changed is not relivant.
14706  *
14707  * Fork - LGPL
14708  * <script type="text/javascript">
14709  */
14710
14711
14712 /**
14713  * @class Roo.data.SortTypes
14714  * @static
14715  * Defines the default sorting (casting?) comparison functions used when sorting data.
14716  */
14717 Roo.data.SortTypes = {
14718     /**
14719      * Default sort that does nothing
14720      * @param {Mixed} s The value being converted
14721      * @return {Mixed} The comparison value
14722      */
14723     none : function(s){
14724         return s;
14725     },
14726     
14727     /**
14728      * The regular expression used to strip tags
14729      * @type {RegExp}
14730      * @property
14731      */
14732     stripTagsRE : /<\/?[^>]+>/gi,
14733     
14734     /**
14735      * Strips all HTML tags to sort on text only
14736      * @param {Mixed} s The value being converted
14737      * @return {String} The comparison value
14738      */
14739     asText : function(s){
14740         return String(s).replace(this.stripTagsRE, "");
14741     },
14742     
14743     /**
14744      * Strips all HTML tags to sort on text only - Case insensitive
14745      * @param {Mixed} s The value being converted
14746      * @return {String} The comparison value
14747      */
14748     asUCText : function(s){
14749         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14750     },
14751     
14752     /**
14753      * Case insensitive string
14754      * @param {Mixed} s The value being converted
14755      * @return {String} The comparison value
14756      */
14757     asUCString : function(s) {
14758         return String(s).toUpperCase();
14759     },
14760     
14761     /**
14762      * Date sorting
14763      * @param {Mixed} s The value being converted
14764      * @return {Number} The comparison value
14765      */
14766     asDate : function(s) {
14767         if(!s){
14768             return 0;
14769         }
14770         if(s instanceof Date){
14771             return s.getTime();
14772         }
14773         return Date.parse(String(s));
14774     },
14775     
14776     /**
14777      * Float sorting
14778      * @param {Mixed} s The value being converted
14779      * @return {Float} The comparison value
14780      */
14781     asFloat : function(s) {
14782         var val = parseFloat(String(s).replace(/,/g, ""));
14783         if(isNaN(val)) {
14784             val = 0;
14785         }
14786         return val;
14787     },
14788     
14789     /**
14790      * Integer sorting
14791      * @param {Mixed} s The value being converted
14792      * @return {Number} The comparison value
14793      */
14794     asInt : function(s) {
14795         var val = parseInt(String(s).replace(/,/g, ""));
14796         if(isNaN(val)) {
14797             val = 0;
14798         }
14799         return val;
14800     }
14801 };/*
14802  * Based on:
14803  * Ext JS Library 1.1.1
14804  * Copyright(c) 2006-2007, Ext JS, LLC.
14805  *
14806  * Originally Released Under LGPL - original licence link has changed is not relivant.
14807  *
14808  * Fork - LGPL
14809  * <script type="text/javascript">
14810  */
14811
14812 /**
14813 * @class Roo.data.Record
14814  * Instances of this class encapsulate both record <em>definition</em> information, and record
14815  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14816  * to access Records cached in an {@link Roo.data.Store} object.<br>
14817  * <p>
14818  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14819  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14820  * objects.<br>
14821  * <p>
14822  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14823  * @constructor
14824  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14825  * {@link #create}. The parameters are the same.
14826  * @param {Array} data An associative Array of data values keyed by the field name.
14827  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14828  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14829  * not specified an integer id is generated.
14830  */
14831 Roo.data.Record = function(data, id){
14832     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14833     this.data = data;
14834 };
14835
14836 /**
14837  * Generate a constructor for a specific record layout.
14838  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14839  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14840  * Each field definition object may contain the following properties: <ul>
14841  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14842  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14843  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14844  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14845  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14846  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14847  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14848  * this may be omitted.</p></li>
14849  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14850  * <ul><li>auto (Default, implies no conversion)</li>
14851  * <li>string</li>
14852  * <li>int</li>
14853  * <li>float</li>
14854  * <li>boolean</li>
14855  * <li>date</li></ul></p></li>
14856  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14857  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14858  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14859  * by the Reader into an object that will be stored in the Record. It is passed the
14860  * following parameters:<ul>
14861  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14862  * </ul></p></li>
14863  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14864  * </ul>
14865  * <br>usage:<br><pre><code>
14866 var TopicRecord = Roo.data.Record.create(
14867     {name: 'title', mapping: 'topic_title'},
14868     {name: 'author', mapping: 'username'},
14869     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14870     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14871     {name: 'lastPoster', mapping: 'user2'},
14872     {name: 'excerpt', mapping: 'post_text'}
14873 );
14874
14875 var myNewRecord = new TopicRecord({
14876     title: 'Do my job please',
14877     author: 'noobie',
14878     totalPosts: 1,
14879     lastPost: new Date(),
14880     lastPoster: 'Animal',
14881     excerpt: 'No way dude!'
14882 });
14883 myStore.add(myNewRecord);
14884 </code></pre>
14885  * @method create
14886  * @static
14887  */
14888 Roo.data.Record.create = function(o){
14889     var f = function(){
14890         f.superclass.constructor.apply(this, arguments);
14891     };
14892     Roo.extend(f, Roo.data.Record);
14893     var p = f.prototype;
14894     p.fields = new Roo.util.MixedCollection(false, function(field){
14895         return field.name;
14896     });
14897     for(var i = 0, len = o.length; i < len; i++){
14898         p.fields.add(new Roo.data.Field(o[i]));
14899     }
14900     f.getField = function(name){
14901         return p.fields.get(name);  
14902     };
14903     return f;
14904 };
14905
14906 Roo.data.Record.AUTO_ID = 1000;
14907 Roo.data.Record.EDIT = 'edit';
14908 Roo.data.Record.REJECT = 'reject';
14909 Roo.data.Record.COMMIT = 'commit';
14910
14911 Roo.data.Record.prototype = {
14912     /**
14913      * Readonly flag - true if this record has been modified.
14914      * @type Boolean
14915      */
14916     dirty : false,
14917     editing : false,
14918     error: null,
14919     modified: null,
14920
14921     // private
14922     join : function(store){
14923         this.store = store;
14924     },
14925
14926     /**
14927      * Set the named field to the specified value.
14928      * @param {String} name The name of the field to set.
14929      * @param {Object} value The value to set the field to.
14930      */
14931     set : function(name, value){
14932         if(this.data[name] == value){
14933             return;
14934         }
14935         this.dirty = true;
14936         if(!this.modified){
14937             this.modified = {};
14938         }
14939         if(typeof this.modified[name] == 'undefined'){
14940             this.modified[name] = this.data[name];
14941         }
14942         this.data[name] = value;
14943         if(!this.editing && this.store){
14944             this.store.afterEdit(this);
14945         }       
14946     },
14947
14948     /**
14949      * Get the value of the named field.
14950      * @param {String} name The name of the field to get the value of.
14951      * @return {Object} The value of the field.
14952      */
14953     get : function(name){
14954         return this.data[name]; 
14955     },
14956
14957     // private
14958     beginEdit : function(){
14959         this.editing = true;
14960         this.modified = {}; 
14961     },
14962
14963     // private
14964     cancelEdit : function(){
14965         this.editing = false;
14966         delete this.modified;
14967     },
14968
14969     // private
14970     endEdit : function(){
14971         this.editing = false;
14972         if(this.dirty && this.store){
14973             this.store.afterEdit(this);
14974         }
14975     },
14976
14977     /**
14978      * Usually called by the {@link Roo.data.Store} which owns the Record.
14979      * Rejects all changes made to the Record since either creation, or the last commit operation.
14980      * Modified fields are reverted to their original values.
14981      * <p>
14982      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14983      * of reject operations.
14984      */
14985     reject : function(){
14986         var m = this.modified;
14987         for(var n in m){
14988             if(typeof m[n] != "function"){
14989                 this.data[n] = m[n];
14990             }
14991         }
14992         this.dirty = false;
14993         delete this.modified;
14994         this.editing = false;
14995         if(this.store){
14996             this.store.afterReject(this);
14997         }
14998     },
14999
15000     /**
15001      * Usually called by the {@link Roo.data.Store} which owns the Record.
15002      * Commits all changes made to the Record since either creation, or the last commit operation.
15003      * <p>
15004      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15005      * of commit operations.
15006      */
15007     commit : function(){
15008         this.dirty = false;
15009         delete this.modified;
15010         this.editing = false;
15011         if(this.store){
15012             this.store.afterCommit(this);
15013         }
15014     },
15015
15016     // private
15017     hasError : function(){
15018         return this.error != null;
15019     },
15020
15021     // private
15022     clearError : function(){
15023         this.error = null;
15024     },
15025
15026     /**
15027      * Creates a copy of this record.
15028      * @param {String} id (optional) A new record id if you don't want to use this record's id
15029      * @return {Record}
15030      */
15031     copy : function(newId) {
15032         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15033     }
15034 };/*
15035  * Based on:
15036  * Ext JS Library 1.1.1
15037  * Copyright(c) 2006-2007, Ext JS, LLC.
15038  *
15039  * Originally Released Under LGPL - original licence link has changed is not relivant.
15040  *
15041  * Fork - LGPL
15042  * <script type="text/javascript">
15043  */
15044
15045
15046
15047 /**
15048  * @class Roo.data.Store
15049  * @extends Roo.util.Observable
15050  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15051  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15052  * <p>
15053  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
15054  * has no knowledge of the format of the data returned by the Proxy.<br>
15055  * <p>
15056  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15057  * instances from the data object. These records are cached and made available through accessor functions.
15058  * @constructor
15059  * Creates a new Store.
15060  * @param {Object} config A config object containing the objects needed for the Store to access data,
15061  * and read the data into Records.
15062  */
15063 Roo.data.Store = function(config){
15064     this.data = new Roo.util.MixedCollection(false);
15065     this.data.getKey = function(o){
15066         return o.id;
15067     };
15068     this.baseParams = {};
15069     // private
15070     this.paramNames = {
15071         "start" : "start",
15072         "limit" : "limit",
15073         "sort" : "sort",
15074         "dir" : "dir",
15075         "multisort" : "_multisort"
15076     };
15077
15078     if(config && config.data){
15079         this.inlineData = config.data;
15080         delete config.data;
15081     }
15082
15083     Roo.apply(this, config);
15084     
15085     if(this.reader){ // reader passed
15086         this.reader = Roo.factory(this.reader, Roo.data);
15087         this.reader.xmodule = this.xmodule || false;
15088         if(!this.recordType){
15089             this.recordType = this.reader.recordType;
15090         }
15091         if(this.reader.onMetaChange){
15092             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15093         }
15094     }
15095
15096     if(this.recordType){
15097         this.fields = this.recordType.prototype.fields;
15098     }
15099     this.modified = [];
15100
15101     this.addEvents({
15102         /**
15103          * @event datachanged
15104          * Fires when the data cache has changed, and a widget which is using this Store
15105          * as a Record cache should refresh its view.
15106          * @param {Store} this
15107          */
15108         datachanged : true,
15109         /**
15110          * @event metachange
15111          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15112          * @param {Store} this
15113          * @param {Object} meta The JSON metadata
15114          */
15115         metachange : true,
15116         /**
15117          * @event add
15118          * Fires when Records have been added to the Store
15119          * @param {Store} this
15120          * @param {Roo.data.Record[]} records The array of Records added
15121          * @param {Number} index The index at which the record(s) were added
15122          */
15123         add : true,
15124         /**
15125          * @event remove
15126          * Fires when a Record has been removed from the Store
15127          * @param {Store} this
15128          * @param {Roo.data.Record} record The Record that was removed
15129          * @param {Number} index The index at which the record was removed
15130          */
15131         remove : true,
15132         /**
15133          * @event update
15134          * Fires when a Record has been updated
15135          * @param {Store} this
15136          * @param {Roo.data.Record} record The Record that was updated
15137          * @param {String} operation The update operation being performed.  Value may be one of:
15138          * <pre><code>
15139  Roo.data.Record.EDIT
15140  Roo.data.Record.REJECT
15141  Roo.data.Record.COMMIT
15142          * </code></pre>
15143          */
15144         update : true,
15145         /**
15146          * @event clear
15147          * Fires when the data cache has been cleared.
15148          * @param {Store} this
15149          */
15150         clear : true,
15151         /**
15152          * @event beforeload
15153          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15154          * the load action will be canceled.
15155          * @param {Store} this
15156          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15157          */
15158         beforeload : true,
15159         /**
15160          * @event beforeloadadd
15161          * Fires after a new set of Records has been loaded.
15162          * @param {Store} this
15163          * @param {Roo.data.Record[]} records The Records that were loaded
15164          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15165          */
15166         beforeloadadd : true,
15167         /**
15168          * @event load
15169          * Fires after a new set of Records has been loaded, before they are added to the store.
15170          * @param {Store} this
15171          * @param {Roo.data.Record[]} records The Records that were loaded
15172          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15173          * @params {Object} return from reader
15174          */
15175         load : true,
15176         /**
15177          * @event loadexception
15178          * Fires if an exception occurs in the Proxy during loading.
15179          * Called with the signature of the Proxy's "loadexception" event.
15180          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15181          * 
15182          * @param {Proxy} 
15183          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15184          * @param {Object} load options 
15185          * @param {Object} jsonData from your request (normally this contains the Exception)
15186          */
15187         loadexception : true
15188     });
15189     
15190     if(this.proxy){
15191         this.proxy = Roo.factory(this.proxy, Roo.data);
15192         this.proxy.xmodule = this.xmodule || false;
15193         this.relayEvents(this.proxy,  ["loadexception"]);
15194     }
15195     this.sortToggle = {};
15196     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15197
15198     Roo.data.Store.superclass.constructor.call(this);
15199
15200     if(this.inlineData){
15201         this.loadData(this.inlineData);
15202         delete this.inlineData;
15203     }
15204 };
15205
15206 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15207      /**
15208     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15209     * without a remote query - used by combo/forms at present.
15210     */
15211     
15212     /**
15213     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15214     */
15215     /**
15216     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15217     */
15218     /**
15219     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15220     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15221     */
15222     /**
15223     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15224     * on any HTTP request
15225     */
15226     /**
15227     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15228     */
15229     /**
15230     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15231     */
15232     multiSort: false,
15233     /**
15234     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15235     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15236     */
15237     remoteSort : false,
15238
15239     /**
15240     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15241      * loaded or when a record is removed. (defaults to false).
15242     */
15243     pruneModifiedRecords : false,
15244
15245     // private
15246     lastOptions : null,
15247
15248     /**
15249      * Add Records to the Store and fires the add event.
15250      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15251      */
15252     add : function(records){
15253         records = [].concat(records);
15254         for(var i = 0, len = records.length; i < len; i++){
15255             records[i].join(this);
15256         }
15257         var index = this.data.length;
15258         this.data.addAll(records);
15259         this.fireEvent("add", this, records, index);
15260     },
15261
15262     /**
15263      * Remove a Record from the Store and fires the remove event.
15264      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15265      */
15266     remove : function(record){
15267         var index = this.data.indexOf(record);
15268         this.data.removeAt(index);
15269  
15270         if(this.pruneModifiedRecords){
15271             this.modified.remove(record);
15272         }
15273         this.fireEvent("remove", this, record, index);
15274     },
15275
15276     /**
15277      * Remove all Records from the Store and fires the clear event.
15278      */
15279     removeAll : function(){
15280         this.data.clear();
15281         if(this.pruneModifiedRecords){
15282             this.modified = [];
15283         }
15284         this.fireEvent("clear", this);
15285     },
15286
15287     /**
15288      * Inserts Records to the Store at the given index and fires the add event.
15289      * @param {Number} index The start index at which to insert the passed Records.
15290      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15291      */
15292     insert : function(index, records){
15293         records = [].concat(records);
15294         for(var i = 0, len = records.length; i < len; i++){
15295             this.data.insert(index, records[i]);
15296             records[i].join(this);
15297         }
15298         this.fireEvent("add", this, records, index);
15299     },
15300
15301     /**
15302      * Get the index within the cache of the passed Record.
15303      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15304      * @return {Number} The index of the passed Record. Returns -1 if not found.
15305      */
15306     indexOf : function(record){
15307         return this.data.indexOf(record);
15308     },
15309
15310     /**
15311      * Get the index within the cache of the Record with the passed id.
15312      * @param {String} id The id of the Record to find.
15313      * @return {Number} The index of the Record. Returns -1 if not found.
15314      */
15315     indexOfId : function(id){
15316         return this.data.indexOfKey(id);
15317     },
15318
15319     /**
15320      * Get the Record with the specified id.
15321      * @param {String} id The id of the Record to find.
15322      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15323      */
15324     getById : function(id){
15325         return this.data.key(id);
15326     },
15327
15328     /**
15329      * Get the Record at the specified index.
15330      * @param {Number} index The index of the Record to find.
15331      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15332      */
15333     getAt : function(index){
15334         return this.data.itemAt(index);
15335     },
15336
15337     /**
15338      * Returns a range of Records between specified indices.
15339      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15340      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15341      * @return {Roo.data.Record[]} An array of Records
15342      */
15343     getRange : function(start, end){
15344         return this.data.getRange(start, end);
15345     },
15346
15347     // private
15348     storeOptions : function(o){
15349         o = Roo.apply({}, o);
15350         delete o.callback;
15351         delete o.scope;
15352         this.lastOptions = o;
15353     },
15354
15355     /**
15356      * Loads the Record cache from the configured Proxy using the configured Reader.
15357      * <p>
15358      * If using remote paging, then the first load call must specify the <em>start</em>
15359      * and <em>limit</em> properties in the options.params property to establish the initial
15360      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15361      * <p>
15362      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15363      * and this call will return before the new data has been loaded. Perform any post-processing
15364      * in a callback function, or in a "load" event handler.</strong>
15365      * <p>
15366      * @param {Object} options An object containing properties which control loading options:<ul>
15367      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15368      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15369      * <pre>
15370                 {
15371                     data : data,  // array of key=>value data like JsonReader
15372                     total : data.length,
15373                     success : true
15374                     
15375                 }
15376         </pre>
15377             }.</li>
15378      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15379      * passed the following arguments:<ul>
15380      * <li>r : Roo.data.Record[]</li>
15381      * <li>options: Options object from the load call</li>
15382      * <li>success: Boolean success indicator</li></ul></li>
15383      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15384      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15385      * </ul>
15386      */
15387     load : function(options){
15388         options = options || {};
15389         if(this.fireEvent("beforeload", this, options) !== false){
15390             this.storeOptions(options);
15391             var p = Roo.apply(options.params || {}, this.baseParams);
15392             // if meta was not loaded from remote source.. try requesting it.
15393             if (!this.reader.metaFromRemote) {
15394                 p._requestMeta = 1;
15395             }
15396             if(this.sortInfo && this.remoteSort){
15397                 var pn = this.paramNames;
15398                 p[pn["sort"]] = this.sortInfo.field;
15399                 p[pn["dir"]] = this.sortInfo.direction;
15400             }
15401             if (this.multiSort) {
15402                 var pn = this.paramNames;
15403                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15404             }
15405             
15406             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15407         }
15408     },
15409
15410     /**
15411      * Reloads the Record cache from the configured Proxy using the configured Reader and
15412      * the options from the last load operation performed.
15413      * @param {Object} options (optional) An object containing properties which may override the options
15414      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15415      * the most recently used options are reused).
15416      */
15417     reload : function(options){
15418         this.load(Roo.applyIf(options||{}, this.lastOptions));
15419     },
15420
15421     // private
15422     // Called as a callback by the Reader during a load operation.
15423     loadRecords : function(o, options, success){
15424          
15425         if(!o){
15426             if(success !== false){
15427                 this.fireEvent("load", this, [], options, o);
15428             }
15429             if(options.callback){
15430                 options.callback.call(options.scope || this, [], options, false);
15431             }
15432             return;
15433         }
15434         // if data returned failure - throw an exception.
15435         if (o.success === false) {
15436             // show a message if no listener is registered.
15437             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15438                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15439             }
15440             // loadmask wil be hooked into this..
15441             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15442             return;
15443         }
15444         var r = o.records, t = o.totalRecords || r.length;
15445         
15446         this.fireEvent("beforeloadadd", this, r, options, o);
15447         
15448         if(!options || options.add !== true){
15449             if(this.pruneModifiedRecords){
15450                 this.modified = [];
15451             }
15452             for(var i = 0, len = r.length; i < len; i++){
15453                 r[i].join(this);
15454             }
15455             if(this.snapshot){
15456                 this.data = this.snapshot;
15457                 delete this.snapshot;
15458             }
15459             this.data.clear();
15460             this.data.addAll(r);
15461             this.totalLength = t;
15462             this.applySort();
15463             this.fireEvent("datachanged", this);
15464         }else{
15465             this.totalLength = Math.max(t, this.data.length+r.length);
15466             this.add(r);
15467         }
15468         
15469         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15470                 
15471             var e = new Roo.data.Record({});
15472
15473             e.set(this.parent.displayField, this.parent.emptyTitle);
15474             e.set(this.parent.valueField, '');
15475
15476             this.insert(0, e);
15477         }
15478             
15479         this.fireEvent("load", this, r, options, o);
15480         if(options.callback){
15481             options.callback.call(options.scope || this, r, options, true);
15482         }
15483     },
15484
15485
15486     /**
15487      * Loads data from a passed data block. A Reader which understands the format of the data
15488      * must have been configured in the constructor.
15489      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15490      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15491      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15492      */
15493     loadData : function(o, append){
15494         var r = this.reader.readRecords(o);
15495         this.loadRecords(r, {add: append}, true);
15496     },
15497     
15498      /**
15499      * using 'cn' the nested child reader read the child array into it's child stores.
15500      * @param {Object} rec The record with a 'children array
15501      */
15502     loadDataFromChildren : function(rec)
15503     {
15504         this.loadData(this.reader.toLoadData(rec));
15505     },
15506     
15507
15508     /**
15509      * Gets the number of cached records.
15510      * <p>
15511      * <em>If using paging, this may not be the total size of the dataset. If the data object
15512      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15513      * the data set size</em>
15514      */
15515     getCount : function(){
15516         return this.data.length || 0;
15517     },
15518
15519     /**
15520      * Gets the total number of records in the dataset as returned by the server.
15521      * <p>
15522      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15523      * the dataset size</em>
15524      */
15525     getTotalCount : function(){
15526         return this.totalLength || 0;
15527     },
15528
15529     /**
15530      * Returns the sort state of the Store as an object with two properties:
15531      * <pre><code>
15532  field {String} The name of the field by which the Records are sorted
15533  direction {String} The sort order, "ASC" or "DESC"
15534      * </code></pre>
15535      */
15536     getSortState : function(){
15537         return this.sortInfo;
15538     },
15539
15540     // private
15541     applySort : function(){
15542         if(this.sortInfo && !this.remoteSort){
15543             var s = this.sortInfo, f = s.field;
15544             var st = this.fields.get(f).sortType;
15545             var fn = function(r1, r2){
15546                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15547                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15548             };
15549             this.data.sort(s.direction, fn);
15550             if(this.snapshot && this.snapshot != this.data){
15551                 this.snapshot.sort(s.direction, fn);
15552             }
15553         }
15554     },
15555
15556     /**
15557      * Sets the default sort column and order to be used by the next load operation.
15558      * @param {String} fieldName The name of the field to sort by.
15559      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15560      */
15561     setDefaultSort : function(field, dir){
15562         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15563     },
15564
15565     /**
15566      * Sort the Records.
15567      * If remote sorting is used, the sort is performed on the server, and the cache is
15568      * reloaded. If local sorting is used, the cache is sorted internally.
15569      * @param {String} fieldName The name of the field to sort by.
15570      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15571      */
15572     sort : function(fieldName, dir){
15573         var f = this.fields.get(fieldName);
15574         if(!dir){
15575             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15576             
15577             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15578                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15579             }else{
15580                 dir = f.sortDir;
15581             }
15582         }
15583         this.sortToggle[f.name] = dir;
15584         this.sortInfo = {field: f.name, direction: dir};
15585         if(!this.remoteSort){
15586             this.applySort();
15587             this.fireEvent("datachanged", this);
15588         }else{
15589             this.load(this.lastOptions);
15590         }
15591     },
15592
15593     /**
15594      * Calls the specified function for each of the Records in the cache.
15595      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15596      * Returning <em>false</em> aborts and exits the iteration.
15597      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15598      */
15599     each : function(fn, scope){
15600         this.data.each(fn, scope);
15601     },
15602
15603     /**
15604      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15605      * (e.g., during paging).
15606      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15607      */
15608     getModifiedRecords : function(){
15609         return this.modified;
15610     },
15611
15612     // private
15613     createFilterFn : function(property, value, anyMatch){
15614         if(!value.exec){ // not a regex
15615             value = String(value);
15616             if(value.length == 0){
15617                 return false;
15618             }
15619             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15620         }
15621         return function(r){
15622             return value.test(r.data[property]);
15623         };
15624     },
15625
15626     /**
15627      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15628      * @param {String} property A field on your records
15629      * @param {Number} start The record index to start at (defaults to 0)
15630      * @param {Number} end The last record index to include (defaults to length - 1)
15631      * @return {Number} The sum
15632      */
15633     sum : function(property, start, end){
15634         var rs = this.data.items, v = 0;
15635         start = start || 0;
15636         end = (end || end === 0) ? end : rs.length-1;
15637
15638         for(var i = start; i <= end; i++){
15639             v += (rs[i].data[property] || 0);
15640         }
15641         return v;
15642     },
15643
15644     /**
15645      * Filter the records by a specified property.
15646      * @param {String} field A field on your records
15647      * @param {String/RegExp} value Either a string that the field
15648      * should start with or a RegExp to test against the field
15649      * @param {Boolean} anyMatch True to match any part not just the beginning
15650      */
15651     filter : function(property, value, anyMatch){
15652         var fn = this.createFilterFn(property, value, anyMatch);
15653         return fn ? this.filterBy(fn) : this.clearFilter();
15654     },
15655
15656     /**
15657      * Filter by a function. The specified function will be called with each
15658      * record in this data source. If the function returns true the record is included,
15659      * otherwise it is filtered.
15660      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15661      * @param {Object} scope (optional) The scope of the function (defaults to this)
15662      */
15663     filterBy : function(fn, scope){
15664         this.snapshot = this.snapshot || this.data;
15665         this.data = this.queryBy(fn, scope||this);
15666         this.fireEvent("datachanged", this);
15667     },
15668
15669     /**
15670      * Query the records by a specified property.
15671      * @param {String} field A field on your records
15672      * @param {String/RegExp} value Either a string that the field
15673      * should start with or a RegExp to test against the field
15674      * @param {Boolean} anyMatch True to match any part not just the beginning
15675      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15676      */
15677     query : function(property, value, anyMatch){
15678         var fn = this.createFilterFn(property, value, anyMatch);
15679         return fn ? this.queryBy(fn) : this.data.clone();
15680     },
15681
15682     /**
15683      * Query by a function. The specified function will be called with each
15684      * record in this data source. If the function returns true the record is included
15685      * in the results.
15686      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15687      * @param {Object} scope (optional) The scope of the function (defaults to this)
15688       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15689      **/
15690     queryBy : function(fn, scope){
15691         var data = this.snapshot || this.data;
15692         return data.filterBy(fn, scope||this);
15693     },
15694
15695     /**
15696      * Collects unique values for a particular dataIndex from this store.
15697      * @param {String} dataIndex The property to collect
15698      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15699      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15700      * @return {Array} An array of the unique values
15701      **/
15702     collect : function(dataIndex, allowNull, bypassFilter){
15703         var d = (bypassFilter === true && this.snapshot) ?
15704                 this.snapshot.items : this.data.items;
15705         var v, sv, r = [], l = {};
15706         for(var i = 0, len = d.length; i < len; i++){
15707             v = d[i].data[dataIndex];
15708             sv = String(v);
15709             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15710                 l[sv] = true;
15711                 r[r.length] = v;
15712             }
15713         }
15714         return r;
15715     },
15716
15717     /**
15718      * Revert to a view of the Record cache with no filtering applied.
15719      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15720      */
15721     clearFilter : function(suppressEvent){
15722         if(this.snapshot && this.snapshot != this.data){
15723             this.data = this.snapshot;
15724             delete this.snapshot;
15725             if(suppressEvent !== true){
15726                 this.fireEvent("datachanged", this);
15727             }
15728         }
15729     },
15730
15731     // private
15732     afterEdit : function(record){
15733         if(this.modified.indexOf(record) == -1){
15734             this.modified.push(record);
15735         }
15736         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15737     },
15738     
15739     // private
15740     afterReject : function(record){
15741         this.modified.remove(record);
15742         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15743     },
15744
15745     // private
15746     afterCommit : function(record){
15747         this.modified.remove(record);
15748         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15749     },
15750
15751     /**
15752      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15753      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15754      */
15755     commitChanges : function(){
15756         var m = this.modified.slice(0);
15757         this.modified = [];
15758         for(var i = 0, len = m.length; i < len; i++){
15759             m[i].commit();
15760         }
15761     },
15762
15763     /**
15764      * Cancel outstanding changes on all changed records.
15765      */
15766     rejectChanges : function(){
15767         var m = this.modified.slice(0);
15768         this.modified = [];
15769         for(var i = 0, len = m.length; i < len; i++){
15770             m[i].reject();
15771         }
15772     },
15773
15774     onMetaChange : function(meta, rtype, o){
15775         this.recordType = rtype;
15776         this.fields = rtype.prototype.fields;
15777         delete this.snapshot;
15778         this.sortInfo = meta.sortInfo || this.sortInfo;
15779         this.modified = [];
15780         this.fireEvent('metachange', this, this.reader.meta);
15781     },
15782     
15783     moveIndex : function(data, type)
15784     {
15785         var index = this.indexOf(data);
15786         
15787         var newIndex = index + type;
15788         
15789         this.remove(data);
15790         
15791         this.insert(newIndex, data);
15792         
15793     }
15794 });/*
15795  * Based on:
15796  * Ext JS Library 1.1.1
15797  * Copyright(c) 2006-2007, Ext JS, LLC.
15798  *
15799  * Originally Released Under LGPL - original licence link has changed is not relivant.
15800  *
15801  * Fork - LGPL
15802  * <script type="text/javascript">
15803  */
15804
15805 /**
15806  * @class Roo.data.SimpleStore
15807  * @extends Roo.data.Store
15808  * Small helper class to make creating Stores from Array data easier.
15809  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15810  * @cfg {Array} fields An array of field definition objects, or field name strings.
15811  * @cfg {Object} an existing reader (eg. copied from another store)
15812  * @cfg {Array} data The multi-dimensional array of data
15813  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15814  * @cfg {Roo.data.Reader} reader  [not-required] 
15815  * @constructor
15816  * @param {Object} config
15817  */
15818 Roo.data.SimpleStore = function(config)
15819 {
15820     Roo.data.SimpleStore.superclass.constructor.call(this, {
15821         isLocal : true,
15822         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15823                 id: config.id
15824             },
15825             Roo.data.Record.create(config.fields)
15826         ),
15827         proxy : new Roo.data.MemoryProxy(config.data)
15828     });
15829     this.load();
15830 };
15831 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15832  * Based on:
15833  * Ext JS Library 1.1.1
15834  * Copyright(c) 2006-2007, Ext JS, LLC.
15835  *
15836  * Originally Released Under LGPL - original licence link has changed is not relivant.
15837  *
15838  * Fork - LGPL
15839  * <script type="text/javascript">
15840  */
15841
15842 /**
15843 /**
15844  * @extends Roo.data.Store
15845  * @class Roo.data.JsonStore
15846  * Small helper class to make creating Stores for JSON data easier. <br/>
15847 <pre><code>
15848 var store = new Roo.data.JsonStore({
15849     url: 'get-images.php',
15850     root: 'images',
15851     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15852 });
15853 </code></pre>
15854  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15855  * JsonReader and HttpProxy (unless inline data is provided).</b>
15856  * @cfg {Array} fields An array of field definition objects, or field name strings.
15857  * @constructor
15858  * @param {Object} config
15859  */
15860 Roo.data.JsonStore = function(c){
15861     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15862         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15863         reader: new Roo.data.JsonReader(c, c.fields)
15864     }));
15865 };
15866 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15867  * Based on:
15868  * Ext JS Library 1.1.1
15869  * Copyright(c) 2006-2007, Ext JS, LLC.
15870  *
15871  * Originally Released Under LGPL - original licence link has changed is not relivant.
15872  *
15873  * Fork - LGPL
15874  * <script type="text/javascript">
15875  */
15876
15877  
15878 Roo.data.Field = function(config){
15879     if(typeof config == "string"){
15880         config = {name: config};
15881     }
15882     Roo.apply(this, config);
15883     
15884     if(!this.type){
15885         this.type = "auto";
15886     }
15887     
15888     var st = Roo.data.SortTypes;
15889     // named sortTypes are supported, here we look them up
15890     if(typeof this.sortType == "string"){
15891         this.sortType = st[this.sortType];
15892     }
15893     
15894     // set default sortType for strings and dates
15895     if(!this.sortType){
15896         switch(this.type){
15897             case "string":
15898                 this.sortType = st.asUCString;
15899                 break;
15900             case "date":
15901                 this.sortType = st.asDate;
15902                 break;
15903             default:
15904                 this.sortType = st.none;
15905         }
15906     }
15907
15908     // define once
15909     var stripRe = /[\$,%]/g;
15910
15911     // prebuilt conversion function for this field, instead of
15912     // switching every time we're reading a value
15913     if(!this.convert){
15914         var cv, dateFormat = this.dateFormat;
15915         switch(this.type){
15916             case "":
15917             case "auto":
15918             case undefined:
15919                 cv = function(v){ return v; };
15920                 break;
15921             case "string":
15922                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15923                 break;
15924             case "int":
15925                 cv = function(v){
15926                     return v !== undefined && v !== null && v !== '' ?
15927                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15928                     };
15929                 break;
15930             case "float":
15931                 cv = function(v){
15932                     return v !== undefined && v !== null && v !== '' ?
15933                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15934                     };
15935                 break;
15936             case "bool":
15937             case "boolean":
15938                 cv = function(v){ return v === true || v === "true" || v == 1; };
15939                 break;
15940             case "date":
15941                 cv = function(v){
15942                     if(!v){
15943                         return '';
15944                     }
15945                     if(v instanceof Date){
15946                         return v;
15947                     }
15948                     if(dateFormat){
15949                         if(dateFormat == "timestamp"){
15950                             return new Date(v*1000);
15951                         }
15952                         return Date.parseDate(v, dateFormat);
15953                     }
15954                     var parsed = Date.parse(v);
15955                     return parsed ? new Date(parsed) : null;
15956                 };
15957              break;
15958             
15959         }
15960         this.convert = cv;
15961     }
15962 };
15963
15964 Roo.data.Field.prototype = {
15965     dateFormat: null,
15966     defaultValue: "",
15967     mapping: null,
15968     sortType : null,
15969     sortDir : "ASC"
15970 };/*
15971  * Based on:
15972  * Ext JS Library 1.1.1
15973  * Copyright(c) 2006-2007, Ext JS, LLC.
15974  *
15975  * Originally Released Under LGPL - original licence link has changed is not relivant.
15976  *
15977  * Fork - LGPL
15978  * <script type="text/javascript">
15979  */
15980  
15981 // Base class for reading structured data from a data source.  This class is intended to be
15982 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15983
15984 /**
15985  * @class Roo.data.DataReader
15986  * @abstract
15987  * Base class for reading structured data from a data source.  This class is intended to be
15988  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15989  */
15990
15991 Roo.data.DataReader = function(meta, recordType){
15992     
15993     this.meta = meta;
15994     
15995     this.recordType = recordType instanceof Array ? 
15996         Roo.data.Record.create(recordType) : recordType;
15997 };
15998
15999 Roo.data.DataReader.prototype = {
16000     
16001     
16002     readerType : 'Data',
16003      /**
16004      * Create an empty record
16005      * @param {Object} data (optional) - overlay some values
16006      * @return {Roo.data.Record} record created.
16007      */
16008     newRow :  function(d) {
16009         var da =  {};
16010         this.recordType.prototype.fields.each(function(c) {
16011             switch( c.type) {
16012                 case 'int' : da[c.name] = 0; break;
16013                 case 'date' : da[c.name] = new Date(); break;
16014                 case 'float' : da[c.name] = 0.0; break;
16015                 case 'boolean' : da[c.name] = false; break;
16016                 default : da[c.name] = ""; break;
16017             }
16018             
16019         });
16020         return new this.recordType(Roo.apply(da, d));
16021     }
16022     
16023     
16024 };/*
16025  * Based on:
16026  * Ext JS Library 1.1.1
16027  * Copyright(c) 2006-2007, Ext JS, LLC.
16028  *
16029  * Originally Released Under LGPL - original licence link has changed is not relivant.
16030  *
16031  * Fork - LGPL
16032  * <script type="text/javascript">
16033  */
16034
16035 /**
16036  * @class Roo.data.DataProxy
16037  * @extends Roo.util.Observable
16038  * @abstract
16039  * This class is an abstract base class for implementations which provide retrieval of
16040  * unformatted data objects.<br>
16041  * <p>
16042  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16043  * (of the appropriate type which knows how to parse the data object) to provide a block of
16044  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16045  * <p>
16046  * Custom implementations must implement the load method as described in
16047  * {@link Roo.data.HttpProxy#load}.
16048  */
16049 Roo.data.DataProxy = function(){
16050     this.addEvents({
16051         /**
16052          * @event beforeload
16053          * Fires before a network request is made to retrieve a data object.
16054          * @param {Object} This DataProxy object.
16055          * @param {Object} params The params parameter to the load function.
16056          */
16057         beforeload : true,
16058         /**
16059          * @event load
16060          * Fires before the load method's callback is called.
16061          * @param {Object} This DataProxy object.
16062          * @param {Object} o The data object.
16063          * @param {Object} arg The callback argument object passed to the load function.
16064          */
16065         load : true,
16066         /**
16067          * @event loadexception
16068          * Fires if an Exception occurs during data retrieval.
16069          * @param {Object} This DataProxy object.
16070          * @param {Object} o The data object.
16071          * @param {Object} arg The callback argument object passed to the load function.
16072          * @param {Object} e The Exception.
16073          */
16074         loadexception : true
16075     });
16076     Roo.data.DataProxy.superclass.constructor.call(this);
16077 };
16078
16079 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16080
16081     /**
16082      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16083      */
16084 /*
16085  * Based on:
16086  * Ext JS Library 1.1.1
16087  * Copyright(c) 2006-2007, Ext JS, LLC.
16088  *
16089  * Originally Released Under LGPL - original licence link has changed is not relivant.
16090  *
16091  * Fork - LGPL
16092  * <script type="text/javascript">
16093  */
16094 /**
16095  * @class Roo.data.MemoryProxy
16096  * @extends Roo.data.DataProxy
16097  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16098  * to the Reader when its load method is called.
16099  * @constructor
16100  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16101  */
16102 Roo.data.MemoryProxy = function(config){
16103     var data = config;
16104     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16105         data = config.data;
16106     }
16107     Roo.data.MemoryProxy.superclass.constructor.call(this);
16108     this.data = data;
16109 };
16110
16111 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16112     
16113     /**
16114      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16115      */
16116     /**
16117      * Load data from the requested source (in this case an in-memory
16118      * data object passed to the constructor), read the data object into
16119      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16120      * process that block using the passed callback.
16121      * @param {Object} params This parameter is not used by the MemoryProxy class.
16122      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16123      * object into a block of Roo.data.Records.
16124      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16125      * The function must be passed <ul>
16126      * <li>The Record block object</li>
16127      * <li>The "arg" argument from the load function</li>
16128      * <li>A boolean success indicator</li>
16129      * </ul>
16130      * @param {Object} scope The scope in which to call the callback
16131      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16132      */
16133     load : function(params, reader, callback, scope, arg){
16134         params = params || {};
16135         var result;
16136         try {
16137             result = reader.readRecords(params.data ? params.data :this.data);
16138         }catch(e){
16139             this.fireEvent("loadexception", this, arg, null, e);
16140             callback.call(scope, null, arg, false);
16141             return;
16142         }
16143         callback.call(scope, result, arg, true);
16144     },
16145     
16146     // private
16147     update : function(params, records){
16148         
16149     }
16150 });/*
16151  * Based on:
16152  * Ext JS Library 1.1.1
16153  * Copyright(c) 2006-2007, Ext JS, LLC.
16154  *
16155  * Originally Released Under LGPL - original licence link has changed is not relivant.
16156  *
16157  * Fork - LGPL
16158  * <script type="text/javascript">
16159  */
16160 /**
16161  * @class Roo.data.HttpProxy
16162  * @extends Roo.data.DataProxy
16163  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16164  * configured to reference a certain URL.<br><br>
16165  * <p>
16166  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16167  * from which the running page was served.<br><br>
16168  * <p>
16169  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16170  * <p>
16171  * Be aware that to enable the browser to parse an XML document, the server must set
16172  * the Content-Type header in the HTTP response to "text/xml".
16173  * @constructor
16174  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16175  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16176  * will be used to make the request.
16177  */
16178 Roo.data.HttpProxy = function(conn){
16179     Roo.data.HttpProxy.superclass.constructor.call(this);
16180     // is conn a conn config or a real conn?
16181     this.conn = conn;
16182     this.useAjax = !conn || !conn.events;
16183   
16184 };
16185
16186 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16187     // thse are take from connection...
16188     
16189     /**
16190      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16191      */
16192     /**
16193      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16194      * extra parameters to each request made by this object. (defaults to undefined)
16195      */
16196     /**
16197      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16198      *  to each request made by this object. (defaults to undefined)
16199      */
16200     /**
16201      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
16202      */
16203     /**
16204      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16205      */
16206      /**
16207      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16208      * @type Boolean
16209      */
16210   
16211
16212     /**
16213      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16214      * @type Boolean
16215      */
16216     /**
16217      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16218      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16219      * a finer-grained basis than the DataProxy events.
16220      */
16221     getConnection : function(){
16222         return this.useAjax ? Roo.Ajax : this.conn;
16223     },
16224
16225     /**
16226      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16227      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16228      * process that block using the passed callback.
16229      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16230      * for the request to the remote server.
16231      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16232      * object into a block of Roo.data.Records.
16233      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16234      * The function must be passed <ul>
16235      * <li>The Record block object</li>
16236      * <li>The "arg" argument from the load function</li>
16237      * <li>A boolean success indicator</li>
16238      * </ul>
16239      * @param {Object} scope The scope in which to call the callback
16240      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16241      */
16242     load : function(params, reader, callback, scope, arg){
16243         if(this.fireEvent("beforeload", this, params) !== false){
16244             var  o = {
16245                 params : params || {},
16246                 request: {
16247                     callback : callback,
16248                     scope : scope,
16249                     arg : arg
16250                 },
16251                 reader: reader,
16252                 callback : this.loadResponse,
16253                 scope: this
16254             };
16255             if(this.useAjax){
16256                 Roo.applyIf(o, this.conn);
16257                 if(this.activeRequest){
16258                     Roo.Ajax.abort(this.activeRequest);
16259                 }
16260                 this.activeRequest = Roo.Ajax.request(o);
16261             }else{
16262                 this.conn.request(o);
16263             }
16264         }else{
16265             callback.call(scope||this, null, arg, false);
16266         }
16267     },
16268
16269     // private
16270     loadResponse : function(o, success, response){
16271         delete this.activeRequest;
16272         if(!success){
16273             this.fireEvent("loadexception", this, o, response);
16274             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16275             return;
16276         }
16277         var result;
16278         try {
16279             result = o.reader.read(response);
16280         }catch(e){
16281             o.success = false;
16282             o.raw = { errorMsg : response.responseText };
16283             this.fireEvent("loadexception", this, o, response, e);
16284             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16285             return;
16286         }
16287         
16288         this.fireEvent("load", this, o, o.request.arg);
16289         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16290     },
16291
16292     // private
16293     update : function(dataSet){
16294
16295     },
16296
16297     // private
16298     updateResponse : function(dataSet){
16299
16300     }
16301 });/*
16302  * Based on:
16303  * Ext JS Library 1.1.1
16304  * Copyright(c) 2006-2007, Ext JS, LLC.
16305  *
16306  * Originally Released Under LGPL - original licence link has changed is not relivant.
16307  *
16308  * Fork - LGPL
16309  * <script type="text/javascript">
16310  */
16311
16312 /**
16313  * @class Roo.data.ScriptTagProxy
16314  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16315  * other than the originating domain of the running page.<br><br>
16316  * <p>
16317  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
16318  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16319  * <p>
16320  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16321  * source code that is used as the source inside a &lt;script> tag.<br><br>
16322  * <p>
16323  * In order for the browser to process the returned data, the server must wrap the data object
16324  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16325  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16326  * depending on whether the callback name was passed:
16327  * <p>
16328  * <pre><code>
16329 boolean scriptTag = false;
16330 String cb = request.getParameter("callback");
16331 if (cb != null) {
16332     scriptTag = true;
16333     response.setContentType("text/javascript");
16334 } else {
16335     response.setContentType("application/x-json");
16336 }
16337 Writer out = response.getWriter();
16338 if (scriptTag) {
16339     out.write(cb + "(");
16340 }
16341 out.print(dataBlock.toJsonString());
16342 if (scriptTag) {
16343     out.write(");");
16344 }
16345 </pre></code>
16346  *
16347  * @constructor
16348  * @param {Object} config A configuration object.
16349  */
16350 Roo.data.ScriptTagProxy = function(config){
16351     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16352     Roo.apply(this, config);
16353     this.head = document.getElementsByTagName("head")[0];
16354 };
16355
16356 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16357
16358 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16359     /**
16360      * @cfg {String} url The URL from which to request the data object.
16361      */
16362     /**
16363      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16364      */
16365     timeout : 30000,
16366     /**
16367      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16368      * the server the name of the callback function set up by the load call to process the returned data object.
16369      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16370      * javascript output which calls this named function passing the data object as its only parameter.
16371      */
16372     callbackParam : "callback",
16373     /**
16374      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16375      * name to the request.
16376      */
16377     nocache : true,
16378
16379     /**
16380      * Load data from the configured URL, read the data object into
16381      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16382      * process that block using the passed callback.
16383      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16384      * for the request to the remote server.
16385      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16386      * object into a block of Roo.data.Records.
16387      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16388      * The function must be passed <ul>
16389      * <li>The Record block object</li>
16390      * <li>The "arg" argument from the load function</li>
16391      * <li>A boolean success indicator</li>
16392      * </ul>
16393      * @param {Object} scope The scope in which to call the callback
16394      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16395      */
16396     load : function(params, reader, callback, scope, arg){
16397         if(this.fireEvent("beforeload", this, params) !== false){
16398
16399             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16400
16401             var url = this.url;
16402             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16403             if(this.nocache){
16404                 url += "&_dc=" + (new Date().getTime());
16405             }
16406             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16407             var trans = {
16408                 id : transId,
16409                 cb : "stcCallback"+transId,
16410                 scriptId : "stcScript"+transId,
16411                 params : params,
16412                 arg : arg,
16413                 url : url,
16414                 callback : callback,
16415                 scope : scope,
16416                 reader : reader
16417             };
16418             var conn = this;
16419
16420             window[trans.cb] = function(o){
16421                 conn.handleResponse(o, trans);
16422             };
16423
16424             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16425
16426             if(this.autoAbort !== false){
16427                 this.abort();
16428             }
16429
16430             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16431
16432             var script = document.createElement("script");
16433             script.setAttribute("src", url);
16434             script.setAttribute("type", "text/javascript");
16435             script.setAttribute("id", trans.scriptId);
16436             this.head.appendChild(script);
16437
16438             this.trans = trans;
16439         }else{
16440             callback.call(scope||this, null, arg, false);
16441         }
16442     },
16443
16444     // private
16445     isLoading : function(){
16446         return this.trans ? true : false;
16447     },
16448
16449     /**
16450      * Abort the current server request.
16451      */
16452     abort : function(){
16453         if(this.isLoading()){
16454             this.destroyTrans(this.trans);
16455         }
16456     },
16457
16458     // private
16459     destroyTrans : function(trans, isLoaded){
16460         this.head.removeChild(document.getElementById(trans.scriptId));
16461         clearTimeout(trans.timeoutId);
16462         if(isLoaded){
16463             window[trans.cb] = undefined;
16464             try{
16465                 delete window[trans.cb];
16466             }catch(e){}
16467         }else{
16468             // if hasn't been loaded, wait for load to remove it to prevent script error
16469             window[trans.cb] = function(){
16470                 window[trans.cb] = undefined;
16471                 try{
16472                     delete window[trans.cb];
16473                 }catch(e){}
16474             };
16475         }
16476     },
16477
16478     // private
16479     handleResponse : function(o, trans){
16480         this.trans = false;
16481         this.destroyTrans(trans, true);
16482         var result;
16483         try {
16484             result = trans.reader.readRecords(o);
16485         }catch(e){
16486             this.fireEvent("loadexception", this, o, trans.arg, e);
16487             trans.callback.call(trans.scope||window, null, trans.arg, false);
16488             return;
16489         }
16490         this.fireEvent("load", this, o, trans.arg);
16491         trans.callback.call(trans.scope||window, result, trans.arg, true);
16492     },
16493
16494     // private
16495     handleFailure : function(trans){
16496         this.trans = false;
16497         this.destroyTrans(trans, false);
16498         this.fireEvent("loadexception", this, null, trans.arg);
16499         trans.callback.call(trans.scope||window, null, trans.arg, false);
16500     }
16501 });/*
16502  * Based on:
16503  * Ext JS Library 1.1.1
16504  * Copyright(c) 2006-2007, Ext JS, LLC.
16505  *
16506  * Originally Released Under LGPL - original licence link has changed is not relivant.
16507  *
16508  * Fork - LGPL
16509  * <script type="text/javascript">
16510  */
16511
16512 /**
16513  * @class Roo.data.JsonReader
16514  * @extends Roo.data.DataReader
16515  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16516  * based on mappings in a provided Roo.data.Record constructor.
16517  * 
16518  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16519  * in the reply previously. 
16520  * 
16521  * <p>
16522  * Example code:
16523  * <pre><code>
16524 var RecordDef = Roo.data.Record.create([
16525     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16526     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16527 ]);
16528 var myReader = new Roo.data.JsonReader({
16529     totalProperty: "results",    // The property which contains the total dataset size (optional)
16530     root: "rows",                // The property which contains an Array of row objects
16531     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16532 }, RecordDef);
16533 </code></pre>
16534  * <p>
16535  * This would consume a JSON file like this:
16536  * <pre><code>
16537 { 'results': 2, 'rows': [
16538     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16539     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16540 }
16541 </code></pre>
16542  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16543  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16544  * paged from the remote server.
16545  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16546  * @cfg {String} root name of the property which contains the Array of row objects.
16547  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16548  * @cfg {Array} fields Array of field definition objects
16549  * @constructor
16550  * Create a new JsonReader
16551  * @param {Object} meta Metadata configuration options
16552  * @param {Object} recordType Either an Array of field definition objects,
16553  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16554  */
16555 Roo.data.JsonReader = function(meta, recordType){
16556     
16557     meta = meta || {};
16558     // set some defaults:
16559     Roo.applyIf(meta, {
16560         totalProperty: 'total',
16561         successProperty : 'success',
16562         root : 'data',
16563         id : 'id'
16564     });
16565     
16566     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16567 };
16568 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16569     
16570     readerType : 'Json',
16571     
16572     /**
16573      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16574      * Used by Store query builder to append _requestMeta to params.
16575      * 
16576      */
16577     metaFromRemote : false,
16578     /**
16579      * This method is only used by a DataProxy which has retrieved data from a remote server.
16580      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16581      * @return {Object} data A data block which is used by an Roo.data.Store object as
16582      * a cache of Roo.data.Records.
16583      */
16584     read : function(response){
16585         var json = response.responseText;
16586        
16587         var o = /* eval:var:o */ eval("("+json+")");
16588         if(!o) {
16589             throw {message: "JsonReader.read: Json object not found"};
16590         }
16591         
16592         if(o.metaData){
16593             
16594             delete this.ef;
16595             this.metaFromRemote = true;
16596             this.meta = o.metaData;
16597             this.recordType = Roo.data.Record.create(o.metaData.fields);
16598             this.onMetaChange(this.meta, this.recordType, o);
16599         }
16600         return this.readRecords(o);
16601     },
16602
16603     // private function a store will implement
16604     onMetaChange : function(meta, recordType, o){
16605
16606     },
16607
16608     /**
16609          * @ignore
16610          */
16611     simpleAccess: function(obj, subsc) {
16612         return obj[subsc];
16613     },
16614
16615         /**
16616          * @ignore
16617          */
16618     getJsonAccessor: function(){
16619         var re = /[\[\.]/;
16620         return function(expr) {
16621             try {
16622                 return(re.test(expr))
16623                     ? new Function("obj", "return obj." + expr)
16624                     : function(obj){
16625                         return obj[expr];
16626                     };
16627             } catch(e){}
16628             return Roo.emptyFn;
16629         };
16630     }(),
16631
16632     /**
16633      * Create a data block containing Roo.data.Records from an XML document.
16634      * @param {Object} o An object which contains an Array of row objects in the property specified
16635      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16636      * which contains the total size of the dataset.
16637      * @return {Object} data A data block which is used by an Roo.data.Store object as
16638      * a cache of Roo.data.Records.
16639      */
16640     readRecords : function(o){
16641         /**
16642          * After any data loads, the raw JSON data is available for further custom processing.
16643          * @type Object
16644          */
16645         this.o = o;
16646         var s = this.meta, Record = this.recordType,
16647             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16648
16649 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16650         if (!this.ef) {
16651             if(s.totalProperty) {
16652                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16653                 }
16654                 if(s.successProperty) {
16655                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16656                 }
16657                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16658                 if (s.id) {
16659                         var g = this.getJsonAccessor(s.id);
16660                         this.getId = function(rec) {
16661                                 var r = g(rec);  
16662                                 return (r === undefined || r === "") ? null : r;
16663                         };
16664                 } else {
16665                         this.getId = function(){return null;};
16666                 }
16667             this.ef = [];
16668             for(var jj = 0; jj < fl; jj++){
16669                 f = fi[jj];
16670                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16671                 this.ef[jj] = this.getJsonAccessor(map);
16672             }
16673         }
16674
16675         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16676         if(s.totalProperty){
16677             var vt = parseInt(this.getTotal(o), 10);
16678             if(!isNaN(vt)){
16679                 totalRecords = vt;
16680             }
16681         }
16682         if(s.successProperty){
16683             var vs = this.getSuccess(o);
16684             if(vs === false || vs === 'false'){
16685                 success = false;
16686             }
16687         }
16688         var records = [];
16689         for(var i = 0; i < c; i++){
16690             var n = root[i];
16691             var values = {};
16692             var id = this.getId(n);
16693             for(var j = 0; j < fl; j++){
16694                 f = fi[j];
16695                                 var v = this.ef[j](n);
16696                                 if (!f.convert) {
16697                                         Roo.log('missing convert for ' + f.name);
16698                                         Roo.log(f);
16699                                         continue;
16700                                 }
16701                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16702             }
16703                         if (!Record) {
16704                                 return {
16705                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16706                                         success : false,
16707                                         records : [],
16708                                         totalRecords : 0
16709                                 };
16710                         }
16711             var record = new Record(values, id);
16712             record.json = n;
16713             records[i] = record;
16714         }
16715         return {
16716             raw : o,
16717             success : success,
16718             records : records,
16719             totalRecords : totalRecords
16720         };
16721     },
16722     // used when loading children.. @see loadDataFromChildren
16723     toLoadData: function(rec)
16724     {
16725         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16726         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16727         return { data : data, total : data.length };
16728         
16729     }
16730 });/*
16731  * Based on:
16732  * Ext JS Library 1.1.1
16733  * Copyright(c) 2006-2007, Ext JS, LLC.
16734  *
16735  * Originally Released Under LGPL - original licence link has changed is not relivant.
16736  *
16737  * Fork - LGPL
16738  * <script type="text/javascript">
16739  */
16740
16741 /**
16742  * @class Roo.data.ArrayReader
16743  * @extends Roo.data.DataReader
16744  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16745  * Each element of that Array represents a row of data fields. The
16746  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16747  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16748  * <p>
16749  * Example code:.
16750  * <pre><code>
16751 var RecordDef = Roo.data.Record.create([
16752     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16753     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16754 ]);
16755 var myReader = new Roo.data.ArrayReader({
16756     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16757 }, RecordDef);
16758 </code></pre>
16759  * <p>
16760  * This would consume an Array like this:
16761  * <pre><code>
16762 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16763   </code></pre>
16764  
16765  * @constructor
16766  * Create a new JsonReader
16767  * @param {Object} meta Metadata configuration options.
16768  * @param {Object|Array} recordType Either an Array of field definition objects
16769  * 
16770  * @cfg {Array} fields Array of field definition objects
16771  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16772  * as specified to {@link Roo.data.Record#create},
16773  * or an {@link Roo.data.Record} object
16774  *
16775  * 
16776  * created using {@link Roo.data.Record#create}.
16777  */
16778 Roo.data.ArrayReader = function(meta, recordType)
16779 {    
16780     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16781 };
16782
16783 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16784     
16785       /**
16786      * Create a data block containing Roo.data.Records from an XML document.
16787      * @param {Object} o An Array of row objects which represents the dataset.
16788      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16789      * a cache of Roo.data.Records.
16790      */
16791     readRecords : function(o)
16792     {
16793         var sid = this.meta ? this.meta.id : null;
16794         var recordType = this.recordType, fields = recordType.prototype.fields;
16795         var records = [];
16796         var root = o;
16797         for(var i = 0; i < root.length; i++){
16798             var n = root[i];
16799             var values = {};
16800             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16801             for(var j = 0, jlen = fields.length; j < jlen; j++){
16802                 var f = fields.items[j];
16803                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16804                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16805                 v = f.convert(v);
16806                 values[f.name] = v;
16807             }
16808             var record = new recordType(values, id);
16809             record.json = n;
16810             records[records.length] = record;
16811         }
16812         return {
16813             records : records,
16814             totalRecords : records.length
16815         };
16816     },
16817     // used when loading children.. @see loadDataFromChildren
16818     toLoadData: function(rec)
16819     {
16820         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16821         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16822         
16823     }
16824     
16825     
16826 });/*
16827  * - LGPL
16828  * * 
16829  */
16830
16831 /**
16832  * @class Roo.bootstrap.form.ComboBox
16833  * @extends Roo.bootstrap.form.TriggerField
16834  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16835  * @cfg {Boolean} append (true|false) default false
16836  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16837  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16838  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16839  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16840  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16841  * @cfg {Boolean} animate default true
16842  * @cfg {Boolean} emptyResultText only for touch device
16843  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16844  * @cfg {String} emptyTitle default ''
16845  * @cfg {Number} width fixed with? experimental
16846  * @constructor
16847  * Create a new ComboBox.
16848  * @param {Object} config Configuration options
16849  */
16850 Roo.bootstrap.form.ComboBox = function(config){
16851     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16852     this.addEvents({
16853         /**
16854          * @event expand
16855          * Fires when the dropdown list is expanded
16856         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16857         */
16858         'expand' : true,
16859         /**
16860          * @event collapse
16861          * Fires when the dropdown list is collapsed
16862         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16863         */
16864         'collapse' : true,
16865         /**
16866          * @event beforeselect
16867          * Fires before a list item is selected. Return false to cancel the selection.
16868         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16869         * @param {Roo.data.Record} record The data record returned from the underlying store
16870         * @param {Number} index The index of the selected item in the dropdown list
16871         */
16872         'beforeselect' : true,
16873         /**
16874          * @event select
16875          * Fires when a list item is selected
16876         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16877         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16878         * @param {Number} index The index of the selected item in the dropdown list
16879         */
16880         'select' : true,
16881         /**
16882          * @event beforequery
16883          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16884          * The event object passed has these properties:
16885         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16886         * @param {String} query The query
16887         * @param {Boolean} forceAll true to force "all" query
16888         * @param {Boolean} cancel true to cancel the query
16889         * @param {Object} e The query event object
16890         */
16891         'beforequery': true,
16892          /**
16893          * @event add
16894          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16895         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16896         */
16897         'add' : true,
16898         /**
16899          * @event edit
16900          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16901         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16903         */
16904         'edit' : true,
16905         /**
16906          * @event remove
16907          * Fires when the remove value from the combobox array
16908         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16909         */
16910         'remove' : true,
16911         /**
16912          * @event afterremove
16913          * Fires when the remove value from the combobox array
16914         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16915         */
16916         'afterremove' : true,
16917         /**
16918          * @event specialfilter
16919          * Fires when specialfilter
16920             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16921             */
16922         'specialfilter' : true,
16923         /**
16924          * @event tick
16925          * Fires when tick the element
16926             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16927             */
16928         'tick' : true,
16929         /**
16930          * @event touchviewdisplay
16931          * Fires when touch view require special display (default is using displayField)
16932             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16933             * @param {Object} cfg set html .
16934             */
16935         'touchviewdisplay' : true
16936         
16937     });
16938     
16939     this.item = [];
16940     this.tickItems = [];
16941     
16942     this.selectedIndex = -1;
16943     if(this.mode == 'local'){
16944         if(config.queryDelay === undefined){
16945             this.queryDelay = 10;
16946         }
16947         if(config.minChars === undefined){
16948             this.minChars = 0;
16949         }
16950     }
16951 };
16952
16953 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16954      
16955     /**
16956      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16957      * rendering into an Roo.Editor, defaults to false)
16958      */
16959     /**
16960      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16961      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16962      */
16963     /**
16964      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16965      */
16966     /**
16967      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16968      * the dropdown list (defaults to undefined, with no header element)
16969      */
16970
16971      /**
16972      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16973      */
16974      
16975      /**
16976      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16977      */
16978     listWidth: undefined,
16979     /**
16980      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16981      * mode = 'remote' or 'text' if mode = 'local')
16982      */
16983     displayField: undefined,
16984     
16985     /**
16986      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16987      * mode = 'remote' or 'value' if mode = 'local'). 
16988      * Note: use of a valueField requires the user make a selection
16989      * in order for a value to be mapped.
16990      */
16991     valueField: undefined,
16992     /**
16993      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16994      */
16995     modalTitle : '',
16996     
16997     /**
16998      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16999      * field's data value (defaults to the underlying DOM element's name)
17000      */
17001     hiddenName: undefined,
17002     /**
17003      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17004      */
17005     listClass: '',
17006     /**
17007      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17008      */
17009     selectedClass: 'active',
17010     
17011     /**
17012      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17013      */
17014     shadow:'sides',
17015     /**
17016      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17017      * anchor positions (defaults to 'tl-bl')
17018      */
17019     listAlign: 'tl-bl?',
17020     /**
17021      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17022      */
17023     maxHeight: 300,
17024     /**
17025      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17026      * query specified by the allQuery config option (defaults to 'query')
17027      */
17028     triggerAction: 'query',
17029     /**
17030      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17031      * (defaults to 4, does not apply if editable = false)
17032      */
17033     minChars : 4,
17034     /**
17035      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17036      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17037      */
17038     typeAhead: false,
17039     /**
17040      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17041      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17042      */
17043     queryDelay: 500,
17044     /**
17045      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17046      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17047      */
17048     pageSize: 0,
17049     /**
17050      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17051      * when editable = true (defaults to false)
17052      */
17053     selectOnFocus:false,
17054     /**
17055      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17056      */
17057     queryParam: 'query',
17058     /**
17059      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17060      * when mode = 'remote' (defaults to 'Loading...')
17061      */
17062     loadingText: 'Loading...',
17063     /**
17064      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17065      */
17066     resizable: false,
17067     /**
17068      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17069      */
17070     handleHeight : 8,
17071     /**
17072      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17073      * traditional select (defaults to true)
17074      */
17075     editable: true,
17076     /**
17077      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17078      */
17079     allQuery: '',
17080     /**
17081      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17082      */
17083     mode: 'remote',
17084     /**
17085      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17086      * listWidth has a higher value)
17087      */
17088     minListWidth : 70,
17089     /**
17090      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17091      * allow the user to set arbitrary text into the field (defaults to false)
17092      */
17093     forceSelection:false,
17094     /**
17095      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17096      * if typeAhead = true (defaults to 250)
17097      */
17098     typeAheadDelay : 250,
17099     /**
17100      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17101      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17102      */
17103     valueNotFoundText : undefined,
17104     /**
17105      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17106      */
17107     blockFocus : false,
17108     
17109     /**
17110      * @cfg {Boolean} disableClear Disable showing of clear button.
17111      */
17112     disableClear : false,
17113     /**
17114      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17115      */
17116     alwaysQuery : false,
17117     
17118     /**
17119      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17120      */
17121     multiple : false,
17122     
17123     /**
17124      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17125      */
17126     invalidClass : "has-warning",
17127     
17128     /**
17129      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17130      */
17131     validClass : "has-success",
17132     
17133     /**
17134      * @cfg {Boolean} specialFilter (true|false) special filter default false
17135      */
17136     specialFilter : false,
17137     
17138     /**
17139      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17140      */
17141     mobileTouchView : true,
17142     
17143     /**
17144      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17145      */
17146     useNativeIOS : false,
17147     
17148     /**
17149      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17150      */
17151     mobile_restrict_height : false,
17152     
17153     ios_options : false,
17154     
17155     //private
17156     addicon : false,
17157     editicon: false,
17158     
17159     page: 0,
17160     hasQuery: false,
17161     append: false,
17162     loadNext: false,
17163     autoFocus : true,
17164     tickable : false,
17165     btnPosition : 'right',
17166     triggerList : true,
17167     showToggleBtn : true,
17168     animate : true,
17169     emptyResultText: 'Empty',
17170     triggerText : 'Select',
17171     emptyTitle : '',
17172     width : false,
17173     
17174     // element that contains real text value.. (when hidden is used..)
17175     
17176     getAutoCreate : function()
17177     {   
17178         var cfg = false;
17179         //render
17180         /*
17181          * Render classic select for iso
17182          */
17183         
17184         if(Roo.isIOS && this.useNativeIOS){
17185             cfg = this.getAutoCreateNativeIOS();
17186             return cfg;
17187         }
17188         
17189         /*
17190          * Touch Devices
17191          */
17192         
17193         if(Roo.isTouch && this.mobileTouchView){
17194             cfg = this.getAutoCreateTouchView();
17195             return cfg;;
17196         }
17197         
17198         /*
17199          *  Normal ComboBox
17200          */
17201         if(!this.tickable){
17202             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17203             return cfg;
17204         }
17205         
17206         /*
17207          *  ComboBox with tickable selections
17208          */
17209              
17210         var align = this.labelAlign || this.parentLabelAlign();
17211         
17212         cfg = {
17213             cls : 'form-group roo-combobox-tickable' //input-group
17214         };
17215         
17216         var btn_text_select = '';
17217         var btn_text_done = '';
17218         var btn_text_cancel = '';
17219         
17220         if (this.btn_text_show) {
17221             btn_text_select = 'Select';
17222             btn_text_done = 'Done';
17223             btn_text_cancel = 'Cancel'; 
17224         }
17225         
17226         var buttons = {
17227             tag : 'div',
17228             cls : 'tickable-buttons',
17229             cn : [
17230                 {
17231                     tag : 'button',
17232                     type : 'button',
17233                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17234                     //html : this.triggerText
17235                     html: btn_text_select
17236                 },
17237                 {
17238                     tag : 'button',
17239                     type : 'button',
17240                     name : 'ok',
17241                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17242                     //html : 'Done'
17243                     html: btn_text_done
17244                 },
17245                 {
17246                     tag : 'button',
17247                     type : 'button',
17248                     name : 'cancel',
17249                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17250                     //html : 'Cancel'
17251                     html: btn_text_cancel
17252                 }
17253             ]
17254         };
17255         
17256         if(this.editable){
17257             buttons.cn.unshift({
17258                 tag: 'input',
17259                 cls: 'roo-select2-search-field-input'
17260             });
17261         }
17262         
17263         var _this = this;
17264         
17265         Roo.each(buttons.cn, function(c){
17266             if (_this.size) {
17267                 c.cls += ' btn-' + _this.size;
17268             }
17269
17270             if (_this.disabled) {
17271                 c.disabled = true;
17272             }
17273         });
17274         
17275         var box = {
17276             tag: 'div',
17277             style : 'display: contents',
17278             cn: [
17279                 {
17280                     tag: 'input',
17281                     type : 'hidden',
17282                     cls: 'form-hidden-field'
17283                 },
17284                 {
17285                     tag: 'ul',
17286                     cls: 'roo-select2-choices',
17287                     cn:[
17288                         {
17289                             tag: 'li',
17290                             cls: 'roo-select2-search-field',
17291                             cn: [
17292                                 buttons
17293                             ]
17294                         }
17295                     ]
17296                 }
17297             ]
17298         };
17299         
17300         var combobox = {
17301             cls: 'roo-select2-container input-group roo-select2-container-multi',
17302             cn: [
17303                 
17304                 box
17305 //                {
17306 //                    tag: 'ul',
17307 //                    cls: 'typeahead typeahead-long dropdown-menu',
17308 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17309 //                }
17310             ]
17311         };
17312         
17313         if(this.hasFeedback && !this.allowBlank){
17314             
17315             var feedback = {
17316                 tag: 'span',
17317                 cls: 'glyphicon form-control-feedback'
17318             };
17319
17320             combobox.cn.push(feedback);
17321         }
17322         
17323         
17324         
17325         var indicator = {
17326             tag : 'i',
17327             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17328             tooltip : 'This field is required'
17329         };
17330         if (Roo.bootstrap.version == 4) {
17331             indicator = {
17332                 tag : 'i',
17333                 style : 'display:none'
17334             };
17335         }
17336         if (align ==='left' && this.fieldLabel.length) {
17337             
17338             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17339             
17340             cfg.cn = [
17341                 indicator,
17342                 {
17343                     tag: 'label',
17344                     'for' :  id,
17345                     cls : 'control-label col-form-label',
17346                     html : this.fieldLabel
17347
17348                 },
17349                 {
17350                     cls : "", 
17351                     cn: [
17352                         combobox
17353                     ]
17354                 }
17355
17356             ];
17357             
17358             var labelCfg = cfg.cn[1];
17359             var contentCfg = cfg.cn[2];
17360             
17361
17362             if(this.indicatorpos == 'right'){
17363                 
17364                 cfg.cn = [
17365                     {
17366                         tag: 'label',
17367                         'for' :  id,
17368                         cls : 'control-label col-form-label',
17369                         cn : [
17370                             {
17371                                 tag : 'span',
17372                                 html : this.fieldLabel
17373                             },
17374                             indicator
17375                         ]
17376                     },
17377                     {
17378                         cls : "",
17379                         cn: [
17380                             combobox
17381                         ]
17382                     }
17383
17384                 ];
17385                 
17386                 
17387                 
17388                 labelCfg = cfg.cn[0];
17389                 contentCfg = cfg.cn[1];
17390             
17391             }
17392             
17393             if(this.labelWidth > 12){
17394                 labelCfg.style = "width: " + this.labelWidth + 'px';
17395             }
17396             if(this.width * 1 > 0){
17397                 contentCfg.style = "width: " + this.width + 'px';
17398             }
17399             if(this.labelWidth < 13 && this.labelmd == 0){
17400                 this.labelmd = this.labelWidth;
17401             }
17402             
17403             if(this.labellg > 0){
17404                 labelCfg.cls += ' col-lg-' + this.labellg;
17405                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17406             }
17407             
17408             if(this.labelmd > 0){
17409                 labelCfg.cls += ' col-md-' + this.labelmd;
17410                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17411             }
17412             
17413             if(this.labelsm > 0){
17414                 labelCfg.cls += ' col-sm-' + this.labelsm;
17415                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17416             }
17417             
17418             if(this.labelxs > 0){
17419                 labelCfg.cls += ' col-xs-' + this.labelxs;
17420                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17421             }
17422                 
17423                 
17424         } else if ( this.fieldLabel.length) {
17425 //                Roo.log(" label");
17426                  cfg.cn = [
17427                    indicator,
17428                     {
17429                         tag: 'label',
17430                         //cls : 'input-group-addon',
17431                         html : this.fieldLabel
17432                     },
17433                     combobox
17434                 ];
17435                 
17436                 if(this.indicatorpos == 'right'){
17437                     cfg.cn = [
17438                         {
17439                             tag: 'label',
17440                             //cls : 'input-group-addon',
17441                             html : this.fieldLabel
17442                         },
17443                         indicator,
17444                         combobox
17445                     ];
17446                     
17447                 }
17448
17449         } else {
17450             
17451 //                Roo.log(" no label && no align");
17452                 cfg = combobox
17453                      
17454                 
17455         }
17456          
17457         var settings=this;
17458         ['xs','sm','md','lg'].map(function(size){
17459             if (settings[size]) {
17460                 cfg.cls += ' col-' + size + '-' + settings[size];
17461             }
17462         });
17463         
17464         return cfg;
17465         
17466     },
17467     
17468     _initEventsCalled : false,
17469     
17470     // private
17471     initEvents: function()
17472     {   
17473         if (this._initEventsCalled) { // as we call render... prevent looping...
17474             return;
17475         }
17476         this._initEventsCalled = true;
17477         
17478         if (!this.store) {
17479             throw "can not find store for combo";
17480         }
17481         
17482         this.indicator = this.indicatorEl();
17483         
17484         this.store = Roo.factory(this.store, Roo.data);
17485         this.store.parent = this;
17486         
17487         // if we are building from html. then this element is so complex, that we can not really
17488         // use the rendered HTML.
17489         // so we have to trash and replace the previous code.
17490         if (Roo.XComponent.build_from_html) {
17491             // remove this element....
17492             var e = this.el.dom, k=0;
17493             while (e ) { e = e.previousSibling;  ++k;}
17494
17495             this.el.remove();
17496             
17497             this.el=false;
17498             this.rendered = false;
17499             
17500             this.render(this.parent().getChildContainer(true), k);
17501         }
17502         
17503         if(Roo.isIOS && this.useNativeIOS){
17504             this.initIOSView();
17505             return;
17506         }
17507         
17508         /*
17509          * Touch Devices
17510          */
17511         
17512         if(Roo.isTouch && this.mobileTouchView){
17513             this.initTouchView();
17514             return;
17515         }
17516         
17517         if(this.tickable){
17518             this.initTickableEvents();
17519             return;
17520         }
17521         
17522         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17523         
17524         if(this.hiddenName){
17525             
17526             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17527             
17528             this.hiddenField.dom.value =
17529                 this.hiddenValue !== undefined ? this.hiddenValue :
17530                 this.value !== undefined ? this.value : '';
17531
17532             // prevent input submission
17533             this.el.dom.removeAttribute('name');
17534             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17535              
17536              
17537         }
17538         //if(Roo.isGecko){
17539         //    this.el.dom.setAttribute('autocomplete', 'off');
17540         //}
17541         
17542         var cls = 'x-combo-list';
17543         
17544         //this.list = new Roo.Layer({
17545         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17546         //});
17547         
17548         var _this = this;
17549         
17550         (function(){
17551             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17552             _this.list.setWidth(lw);
17553         }).defer(100);
17554         
17555         this.list.on('mouseover', this.onViewOver, this);
17556         this.list.on('mousemove', this.onViewMove, this);
17557         this.list.on('scroll', this.onViewScroll, this);
17558         
17559         /*
17560         this.list.swallowEvent('mousewheel');
17561         this.assetHeight = 0;
17562
17563         if(this.title){
17564             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17565             this.assetHeight += this.header.getHeight();
17566         }
17567
17568         this.innerList = this.list.createChild({cls:cls+'-inner'});
17569         this.innerList.on('mouseover', this.onViewOver, this);
17570         this.innerList.on('mousemove', this.onViewMove, this);
17571         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17572         
17573         if(this.allowBlank && !this.pageSize && !this.disableClear){
17574             this.footer = this.list.createChild({cls:cls+'-ft'});
17575             this.pageTb = new Roo.Toolbar(this.footer);
17576            
17577         }
17578         if(this.pageSize){
17579             this.footer = this.list.createChild({cls:cls+'-ft'});
17580             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17581                     {pageSize: this.pageSize});
17582             
17583         }
17584         
17585         if (this.pageTb && this.allowBlank && !this.disableClear) {
17586             var _this = this;
17587             this.pageTb.add(new Roo.Toolbar.Fill(), {
17588                 cls: 'x-btn-icon x-btn-clear',
17589                 text: '&#160;',
17590                 handler: function()
17591                 {
17592                     _this.collapse();
17593                     _this.clearValue();
17594                     _this.onSelect(false, -1);
17595                 }
17596             });
17597         }
17598         if (this.footer) {
17599             this.assetHeight += this.footer.getHeight();
17600         }
17601         */
17602             
17603         if(!this.tpl){
17604             this.tpl = Roo.bootstrap.version == 4 ?
17605                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17606                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17607         }
17608
17609         this.view = new Roo.View(this.list, this.tpl, {
17610             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17611         });
17612         //this.view.wrapEl.setDisplayed(false);
17613         this.view.on('click', this.onViewClick, this);
17614         
17615         
17616         this.store.on('beforeload', this.onBeforeLoad, this);
17617         this.store.on('load', this.onLoad, this);
17618         this.store.on('loadexception', this.onLoadException, this);
17619         /*
17620         if(this.resizable){
17621             this.resizer = new Roo.Resizable(this.list,  {
17622                pinned:true, handles:'se'
17623             });
17624             this.resizer.on('resize', function(r, w, h){
17625                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17626                 this.listWidth = w;
17627                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17628                 this.restrictHeight();
17629             }, this);
17630             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17631         }
17632         */
17633         if(!this.editable){
17634             this.editable = true;
17635             this.setEditable(false);
17636         }
17637         
17638         /*
17639         
17640         if (typeof(this.events.add.listeners) != 'undefined') {
17641             
17642             this.addicon = this.wrap.createChild(
17643                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17644        
17645             this.addicon.on('click', function(e) {
17646                 this.fireEvent('add', this);
17647             }, this);
17648         }
17649         if (typeof(this.events.edit.listeners) != 'undefined') {
17650             
17651             this.editicon = this.wrap.createChild(
17652                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17653             if (this.addicon) {
17654                 this.editicon.setStyle('margin-left', '40px');
17655             }
17656             this.editicon.on('click', function(e) {
17657                 
17658                 // we fire even  if inothing is selected..
17659                 this.fireEvent('edit', this, this.lastData );
17660                 
17661             }, this);
17662         }
17663         */
17664         
17665         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17666             "up" : function(e){
17667                 this.inKeyMode = true;
17668                 this.selectPrev();
17669             },
17670
17671             "down" : function(e){
17672                 if(!this.isExpanded()){
17673                     this.onTriggerClick();
17674                 }else{
17675                     this.inKeyMode = true;
17676                     this.selectNext();
17677                 }
17678             },
17679
17680             "enter" : function(e){
17681 //                this.onViewClick();
17682                 //return true;
17683                 this.collapse();
17684                 
17685                 if(this.fireEvent("specialkey", this, e)){
17686                     this.onViewClick(false);
17687                 }
17688                 
17689                 return true;
17690             },
17691
17692             "esc" : function(e){
17693                 this.collapse();
17694             },
17695
17696             "tab" : function(e){
17697                 this.collapse();
17698                 
17699                 if(this.fireEvent("specialkey", this, e)){
17700                     this.onViewClick(false);
17701                 }
17702                 
17703                 return true;
17704             },
17705
17706             scope : this,
17707
17708             doRelay : function(foo, bar, hname){
17709                 if(hname == 'down' || this.scope.isExpanded()){
17710                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17711                 }
17712                 return true;
17713             },
17714
17715             forceKeyDown: true
17716         });
17717         
17718         
17719         this.queryDelay = Math.max(this.queryDelay || 10,
17720                 this.mode == 'local' ? 10 : 250);
17721         
17722         
17723         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17724         
17725         if(this.typeAhead){
17726             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17727         }
17728         if(this.editable !== false){
17729             this.inputEl().on("keyup", this.onKeyUp, this);
17730         }
17731         if(this.forceSelection){
17732             this.inputEl().on('blur', this.doForce, this);
17733         }
17734         
17735         if(this.multiple){
17736             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17737             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17738         }
17739     },
17740     
17741     initTickableEvents: function()
17742     {   
17743         this.createList();
17744         
17745         if(this.hiddenName){
17746             
17747             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17748             
17749             this.hiddenField.dom.value =
17750                 this.hiddenValue !== undefined ? this.hiddenValue :
17751                 this.value !== undefined ? this.value : '';
17752
17753             // prevent input submission
17754             this.el.dom.removeAttribute('name');
17755             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17756              
17757              
17758         }
17759         
17760 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17761         
17762         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17763         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17764         if(this.triggerList){
17765             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17766         }
17767          
17768         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17769         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17770         
17771         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17772         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17773         
17774         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17775         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17776         
17777         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17778         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17779         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17780         
17781         this.okBtn.hide();
17782         this.cancelBtn.hide();
17783         
17784         var _this = this;
17785         
17786         (function(){
17787             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17788             _this.list.setWidth(lw);
17789         }).defer(100);
17790         
17791         this.list.on('mouseover', this.onViewOver, this);
17792         this.list.on('mousemove', this.onViewMove, this);
17793         
17794         this.list.on('scroll', this.onViewScroll, this);
17795         
17796         if(!this.tpl){
17797             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17798                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17799         }
17800
17801         this.view = new Roo.View(this.list, this.tpl, {
17802             singleSelect:true,
17803             tickable:true,
17804             parent:this,
17805             store: this.store,
17806             selectedClass: this.selectedClass
17807         });
17808         
17809         //this.view.wrapEl.setDisplayed(false);
17810         this.view.on('click', this.onViewClick, this);
17811         
17812         
17813         
17814         this.store.on('beforeload', this.onBeforeLoad, this);
17815         this.store.on('load', this.onLoad, this);
17816         this.store.on('loadexception', this.onLoadException, this);
17817         
17818         if(this.editable){
17819             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17820                 "up" : function(e){
17821                     this.inKeyMode = true;
17822                     this.selectPrev();
17823                 },
17824
17825                 "down" : function(e){
17826                     this.inKeyMode = true;
17827                     this.selectNext();
17828                 },
17829
17830                 "enter" : function(e){
17831                     if(this.fireEvent("specialkey", this, e)){
17832                         this.onViewClick(false);
17833                     }
17834                     
17835                     return true;
17836                 },
17837
17838                 "esc" : function(e){
17839                     this.onTickableFooterButtonClick(e, false, false);
17840                 },
17841
17842                 "tab" : function(e){
17843                     this.fireEvent("specialkey", this, e);
17844                     
17845                     this.onTickableFooterButtonClick(e, false, false);
17846                     
17847                     return true;
17848                 },
17849
17850                 scope : this,
17851
17852                 doRelay : function(e, fn, key){
17853                     if(this.scope.isExpanded()){
17854                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17855                     }
17856                     return true;
17857                 },
17858
17859                 forceKeyDown: true
17860             });
17861         }
17862         
17863         this.queryDelay = Math.max(this.queryDelay || 10,
17864                 this.mode == 'local' ? 10 : 250);
17865         
17866         
17867         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17868         
17869         if(this.typeAhead){
17870             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17871         }
17872         
17873         if(this.editable !== false){
17874             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17875         }
17876         
17877         this.indicator = this.indicatorEl();
17878         
17879         if(this.indicator){
17880             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17881             this.indicator.hide();
17882         }
17883         
17884     },
17885
17886     onDestroy : function(){
17887         if(this.view){
17888             this.view.setStore(null);
17889             this.view.el.removeAllListeners();
17890             this.view.el.remove();
17891             this.view.purgeListeners();
17892         }
17893         if(this.list){
17894             this.list.dom.innerHTML  = '';
17895         }
17896         
17897         if(this.store){
17898             this.store.un('beforeload', this.onBeforeLoad, this);
17899             this.store.un('load', this.onLoad, this);
17900             this.store.un('loadexception', this.onLoadException, this);
17901         }
17902         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17903     },
17904
17905     // private
17906     fireKey : function(e){
17907         if(e.isNavKeyPress() && !this.list.isVisible()){
17908             this.fireEvent("specialkey", this, e);
17909         }
17910     },
17911
17912     // private
17913     onResize: function(w, h)
17914     {
17915         
17916         
17917 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17918 //        
17919 //        if(typeof w != 'number'){
17920 //            // we do not handle it!?!?
17921 //            return;
17922 //        }
17923 //        var tw = this.trigger.getWidth();
17924 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17925 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17926 //        var x = w - tw;
17927 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17928 //            
17929 //        //this.trigger.setStyle('left', x+'px');
17930 //        
17931 //        if(this.list && this.listWidth === undefined){
17932 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17933 //            this.list.setWidth(lw);
17934 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17935 //        }
17936         
17937     
17938         
17939     },
17940
17941     /**
17942      * Allow or prevent the user from directly editing the field text.  If false is passed,
17943      * the user will only be able to select from the items defined in the dropdown list.  This method
17944      * is the runtime equivalent of setting the 'editable' config option at config time.
17945      * @param {Boolean} value True to allow the user to directly edit the field text
17946      */
17947     setEditable : function(value){
17948         if(value == this.editable){
17949             return;
17950         }
17951         this.editable = value;
17952         if(!value){
17953             this.inputEl().dom.setAttribute('readOnly', true);
17954             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17955             this.inputEl().addClass('x-combo-noedit');
17956         }else{
17957             this.inputEl().dom.removeAttribute('readOnly');
17958             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17959             this.inputEl().removeClass('x-combo-noedit');
17960         }
17961     },
17962
17963     // private
17964     
17965     onBeforeLoad : function(combo,opts){
17966         if(!this.hasFocus){
17967             return;
17968         }
17969          if (!opts.add) {
17970             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17971          }
17972         this.restrictHeight();
17973         this.selectedIndex = -1;
17974     },
17975
17976     // private
17977     onLoad : function(){
17978         
17979         this.hasQuery = false;
17980         
17981         if(!this.hasFocus){
17982             return;
17983         }
17984         
17985         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17986             this.loading.hide();
17987         }
17988         
17989         if(this.store.getCount() > 0){
17990             
17991             this.expand();
17992             this.restrictHeight();
17993             if(this.lastQuery == this.allQuery){
17994                 if(this.editable && !this.tickable){
17995                     this.inputEl().dom.select();
17996                 }
17997                 
17998                 if(
17999                     !this.selectByValue(this.value, true) &&
18000                     this.autoFocus && 
18001                     (
18002                         !this.store.lastOptions ||
18003                         typeof(this.store.lastOptions.add) == 'undefined' || 
18004                         this.store.lastOptions.add != true
18005                     )
18006                 ){
18007                     this.select(0, true);
18008                 }
18009             }else{
18010                 if(this.autoFocus){
18011                     this.selectNext();
18012                 }
18013                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18014                     this.taTask.delay(this.typeAheadDelay);
18015                 }
18016             }
18017         }else{
18018             this.onEmptyResults();
18019         }
18020         
18021         //this.el.focus();
18022     },
18023     // private
18024     onLoadException : function()
18025     {
18026         this.hasQuery = false;
18027         
18028         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18029             this.loading.hide();
18030         }
18031         
18032         if(this.tickable && this.editable){
18033             return;
18034         }
18035         
18036         this.collapse();
18037         // only causes errors at present
18038         //Roo.log(this.store.reader.jsonData);
18039         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18040             // fixme
18041             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18042         //}
18043         
18044         
18045     },
18046     // private
18047     onTypeAhead : function(){
18048         if(this.store.getCount() > 0){
18049             var r = this.store.getAt(0);
18050             var newValue = r.data[this.displayField];
18051             var len = newValue.length;
18052             var selStart = this.getRawValue().length;
18053             
18054             if(selStart != len){
18055                 this.setRawValue(newValue);
18056                 this.selectText(selStart, newValue.length);
18057             }
18058         }
18059     },
18060
18061     // private
18062     onSelect : function(record, index){
18063         
18064         if(this.fireEvent('beforeselect', this, record, index) !== false){
18065         
18066             this.setFromData(index > -1 ? record.data : false);
18067             
18068             this.collapse();
18069             this.fireEvent('select', this, record, index);
18070         }
18071     },
18072
18073     /**
18074      * Returns the currently selected field value or empty string if no value is set.
18075      * @return {String} value The selected value
18076      */
18077     getValue : function()
18078     {
18079         if(Roo.isIOS && this.useNativeIOS){
18080             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18081         }
18082         
18083         if(this.multiple){
18084             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18085         }
18086         
18087         if(this.valueField){
18088             return typeof this.value != 'undefined' ? this.value : '';
18089         }else{
18090             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18091         }
18092     },
18093     
18094     getRawValue : function()
18095     {
18096         if(Roo.isIOS && this.useNativeIOS){
18097             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18098         }
18099         
18100         var v = this.inputEl().getValue();
18101         
18102         return v;
18103     },
18104
18105     /**
18106      * Clears any text/value currently set in the field
18107      */
18108     clearValue : function(){
18109         
18110         if(this.hiddenField){
18111             this.hiddenField.dom.value = '';
18112         }
18113         this.value = '';
18114         this.setRawValue('');
18115         this.lastSelectionText = '';
18116         this.lastData = false;
18117         
18118         var close = this.closeTriggerEl();
18119         
18120         if(close){
18121             close.hide();
18122         }
18123         
18124         this.validate();
18125         
18126     },
18127
18128     /**
18129      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18130      * will be displayed in the field.  If the value does not match the data value of an existing item,
18131      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18132      * Otherwise the field will be blank (although the value will still be set).
18133      * @param {String} value The value to match
18134      */
18135     setValue : function(v)
18136     {
18137         if(Roo.isIOS && this.useNativeIOS){
18138             this.setIOSValue(v);
18139             return;
18140         }
18141         
18142         if(this.multiple){
18143             this.syncValue();
18144             return;
18145         }
18146         
18147         var text = v;
18148         if(this.valueField){
18149             var r = this.findRecord(this.valueField, v);
18150             if(r){
18151                 text = r.data[this.displayField];
18152             }else if(this.valueNotFoundText !== undefined){
18153                 text = this.valueNotFoundText;
18154             }
18155         }
18156         this.lastSelectionText = text;
18157         if(this.hiddenField){
18158             this.hiddenField.dom.value = v;
18159         }
18160         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18161         this.value = v;
18162         
18163         var close = this.closeTriggerEl();
18164         
18165         if(close){
18166             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18167         }
18168         
18169         this.validate();
18170     },
18171     /**
18172      * @property {Object} the last set data for the element
18173      */
18174     
18175     lastData : false,
18176     /**
18177      * Sets the value of the field based on a object which is related to the record format for the store.
18178      * @param {Object} value the value to set as. or false on reset?
18179      */
18180     setFromData : function(o){
18181         
18182         if(this.multiple){
18183             this.addItem(o);
18184             return;
18185         }
18186             
18187         var dv = ''; // display value
18188         var vv = ''; // value value..
18189         this.lastData = o;
18190         if (this.displayField) {
18191             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18192         } else {
18193             // this is an error condition!!!
18194             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18195         }
18196         
18197         if(this.valueField){
18198             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18199         }
18200         
18201         var close = this.closeTriggerEl();
18202         
18203         if(close){
18204             if(dv.length || vv * 1 > 0){
18205                 close.show() ;
18206                 this.blockFocus=true;
18207             } else {
18208                 close.hide();
18209             }             
18210         }
18211         
18212         if(this.hiddenField){
18213             this.hiddenField.dom.value = vv;
18214             
18215             this.lastSelectionText = dv;
18216             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18217             this.value = vv;
18218             return;
18219         }
18220         // no hidden field.. - we store the value in 'value', but still display
18221         // display field!!!!
18222         this.lastSelectionText = dv;
18223         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18224         this.value = vv;
18225         
18226         
18227         
18228     },
18229     // private
18230     reset : function(){
18231         // overridden so that last data is reset..
18232         
18233         if(this.multiple){
18234             this.clearItem();
18235             return;
18236         }
18237         
18238         this.setValue(this.originalValue);
18239         //this.clearInvalid();
18240         this.lastData = false;
18241         if (this.view) {
18242             this.view.clearSelections();
18243         }
18244         
18245         this.validate();
18246     },
18247     // private
18248     findRecord : function(prop, value){
18249         var record;
18250         if(this.store.getCount() > 0){
18251             this.store.each(function(r){
18252                 if(r.data[prop] == value){
18253                     record = r;
18254                     return false;
18255                 }
18256                 return true;
18257             });
18258         }
18259         return record;
18260     },
18261     
18262     getName: function()
18263     {
18264         // returns hidden if it's set..
18265         if (!this.rendered) {return ''};
18266         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18267         
18268     },
18269     // private
18270     onViewMove : function(e, t){
18271         this.inKeyMode = false;
18272     },
18273
18274     // private
18275     onViewOver : function(e, t){
18276         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18277             return;
18278         }
18279         var item = this.view.findItemFromChild(t);
18280         
18281         if(item){
18282             var index = this.view.indexOf(item);
18283             this.select(index, false);
18284         }
18285     },
18286
18287     // private
18288     onViewClick : function(view, doFocus, el, e)
18289     {
18290         var index = this.view.getSelectedIndexes()[0];
18291         
18292         var r = this.store.getAt(index);
18293         
18294         if(this.tickable){
18295             
18296             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18297                 return;
18298             }
18299             
18300             var rm = false;
18301             var _this = this;
18302             
18303             Roo.each(this.tickItems, function(v,k){
18304                 
18305                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18306                     Roo.log(v);
18307                     _this.tickItems.splice(k, 1);
18308                     
18309                     if(typeof(e) == 'undefined' && view == false){
18310                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18311                     }
18312                     
18313                     rm = true;
18314                     return;
18315                 }
18316             });
18317             
18318             if(rm){
18319                 return;
18320             }
18321             
18322             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18323                 this.tickItems.push(r.data);
18324             }
18325             
18326             if(typeof(e) == 'undefined' && view == false){
18327                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18328             }
18329                     
18330             return;
18331         }
18332         
18333         if(r){
18334             this.onSelect(r, index);
18335         }
18336         if(doFocus !== false && !this.blockFocus){
18337             this.inputEl().focus();
18338         }
18339     },
18340
18341     // private
18342     restrictHeight : function(){
18343         //this.innerList.dom.style.height = '';
18344         //var inner = this.innerList.dom;
18345         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18346         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18347         //this.list.beginUpdate();
18348         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18349         this.list.alignTo(this.inputEl(), this.listAlign);
18350         this.list.alignTo(this.inputEl(), this.listAlign);
18351         //this.list.endUpdate();
18352     },
18353
18354     // private
18355     onEmptyResults : function(){
18356         
18357         if(this.tickable && this.editable){
18358             this.hasFocus = false;
18359             this.restrictHeight();
18360             return;
18361         }
18362         
18363         this.collapse();
18364     },
18365
18366     /**
18367      * Returns true if the dropdown list is expanded, else false.
18368      */
18369     isExpanded : function(){
18370         return this.list.isVisible();
18371     },
18372
18373     /**
18374      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18375      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18376      * @param {String} value The data value of the item to select
18377      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18378      * selected item if it is not currently in view (defaults to true)
18379      * @return {Boolean} True if the value matched an item in the list, else false
18380      */
18381     selectByValue : function(v, scrollIntoView){
18382         if(v !== undefined && v !== null){
18383             var r = this.findRecord(this.valueField || this.displayField, v);
18384             if(r){
18385                 this.select(this.store.indexOf(r), scrollIntoView);
18386                 return true;
18387             }
18388         }
18389         return false;
18390     },
18391
18392     /**
18393      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18394      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18395      * @param {Number} index The zero-based index of the list item to select
18396      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18397      * selected item if it is not currently in view (defaults to true)
18398      */
18399     select : function(index, scrollIntoView){
18400         this.selectedIndex = index;
18401         this.view.select(index);
18402         if(scrollIntoView !== false){
18403             var el = this.view.getNode(index);
18404             /*
18405              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18406              */
18407             if(el){
18408                 this.list.scrollChildIntoView(el, false);
18409             }
18410         }
18411     },
18412
18413     // private
18414     selectNext : function(){
18415         var ct = this.store.getCount();
18416         if(ct > 0){
18417             if(this.selectedIndex == -1){
18418                 this.select(0);
18419             }else if(this.selectedIndex < ct-1){
18420                 this.select(this.selectedIndex+1);
18421             }
18422         }
18423     },
18424
18425     // private
18426     selectPrev : function(){
18427         var ct = this.store.getCount();
18428         if(ct > 0){
18429             if(this.selectedIndex == -1){
18430                 this.select(0);
18431             }else if(this.selectedIndex != 0){
18432                 this.select(this.selectedIndex-1);
18433             }
18434         }
18435     },
18436
18437     // private
18438     onKeyUp : function(e){
18439         if(this.editable !== false && !e.isSpecialKey()){
18440             this.lastKey = e.getKey();
18441             this.dqTask.delay(this.queryDelay);
18442         }
18443     },
18444
18445     // private
18446     validateBlur : function(){
18447         return !this.list || !this.list.isVisible();   
18448     },
18449
18450     // private
18451     initQuery : function(){
18452         
18453         var v = this.getRawValue();
18454         
18455         if(this.tickable && this.editable){
18456             v = this.tickableInputEl().getValue();
18457         }
18458         
18459         this.doQuery(v);
18460     },
18461
18462     // private
18463     doForce : function(){
18464         if(this.inputEl().dom.value.length > 0){
18465             this.inputEl().dom.value =
18466                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18467              
18468         }
18469     },
18470
18471     /**
18472      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18473      * query allowing the query action to be canceled if needed.
18474      * @param {String} query The SQL query to execute
18475      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18476      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18477      * saved in the current store (defaults to false)
18478      */
18479     doQuery : function(q, forceAll){
18480         
18481         if(q === undefined || q === null){
18482             q = '';
18483         }
18484         var qe = {
18485             query: q,
18486             forceAll: forceAll,
18487             combo: this,
18488             cancel:false
18489         };
18490         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18491             return false;
18492         }
18493         q = qe.query;
18494         
18495         forceAll = qe.forceAll;
18496         if(forceAll === true || (q.length >= this.minChars)){
18497             
18498             this.hasQuery = true;
18499             
18500             if(this.lastQuery != q || this.alwaysQuery){
18501                 this.lastQuery = q;
18502                 if(this.mode == 'local'){
18503                     this.selectedIndex = -1;
18504                     if(forceAll){
18505                         this.store.clearFilter();
18506                     }else{
18507                         
18508                         if(this.specialFilter){
18509                             this.fireEvent('specialfilter', this);
18510                             this.onLoad();
18511                             return;
18512                         }
18513                         
18514                         this.store.filter(this.displayField, q);
18515                     }
18516                     
18517                     this.store.fireEvent("datachanged", this.store);
18518                     
18519                     this.onLoad();
18520                     
18521                     
18522                 }else{
18523                     
18524                     this.store.baseParams[this.queryParam] = q;
18525                     
18526                     var options = {params : this.getParams(q)};
18527                     
18528                     if(this.loadNext){
18529                         options.add = true;
18530                         options.params.start = this.page * this.pageSize;
18531                     }
18532                     
18533                     this.store.load(options);
18534                     
18535                     /*
18536                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18537                      *  we should expand the list on onLoad
18538                      *  so command out it
18539                      */
18540 //                    this.expand();
18541                 }
18542             }else{
18543                 this.selectedIndex = -1;
18544                 this.onLoad();   
18545             }
18546         }
18547         
18548         this.loadNext = false;
18549     },
18550     
18551     // private
18552     getParams : function(q){
18553         var p = {};
18554         //p[this.queryParam] = q;
18555         
18556         if(this.pageSize){
18557             p.start = 0;
18558             p.limit = this.pageSize;
18559         }
18560         return p;
18561     },
18562
18563     /**
18564      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18565      */
18566     collapse : function(){
18567         if(!this.isExpanded()){
18568             return;
18569         }
18570         
18571         this.list.hide();
18572         
18573         this.hasFocus = false;
18574         
18575         if(this.tickable){
18576             this.okBtn.hide();
18577             this.cancelBtn.hide();
18578             this.trigger.show();
18579             
18580             if(this.editable){
18581                 this.tickableInputEl().dom.value = '';
18582                 this.tickableInputEl().blur();
18583             }
18584             
18585         }
18586         
18587         Roo.get(document).un('mousedown', this.collapseIf, this);
18588         Roo.get(document).un('mousewheel', this.collapseIf, this);
18589         if (!this.editable) {
18590             Roo.get(document).un('keydown', this.listKeyPress, this);
18591         }
18592         this.fireEvent('collapse', this);
18593         
18594         this.validate();
18595     },
18596
18597     // private
18598     collapseIf : function(e){
18599         var in_combo  = e.within(this.el);
18600         var in_list =  e.within(this.list);
18601         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18602         
18603         if (in_combo || in_list || is_list) {
18604             //e.stopPropagation();
18605             return;
18606         }
18607         
18608         if(this.tickable){
18609             this.onTickableFooterButtonClick(e, false, false);
18610         }
18611
18612         this.collapse();
18613         
18614     },
18615
18616     /**
18617      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18618      */
18619     expand : function(){
18620        
18621         if(this.isExpanded() || !this.hasFocus){
18622             return;
18623         }
18624         
18625         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18626         this.list.setWidth(lw);
18627         
18628         Roo.log('expand');
18629         
18630         this.list.show();
18631         
18632         this.restrictHeight();
18633         
18634         if(this.tickable){
18635             
18636             this.tickItems = Roo.apply([], this.item);
18637             
18638             this.okBtn.show();
18639             this.cancelBtn.show();
18640             this.trigger.hide();
18641             
18642             if(this.editable){
18643                 this.tickableInputEl().focus();
18644             }
18645             
18646         }
18647         
18648         Roo.get(document).on('mousedown', this.collapseIf, this);
18649         Roo.get(document).on('mousewheel', this.collapseIf, this);
18650         if (!this.editable) {
18651             Roo.get(document).on('keydown', this.listKeyPress, this);
18652         }
18653         
18654         this.fireEvent('expand', this);
18655     },
18656
18657     // private
18658     // Implements the default empty TriggerField.onTriggerClick function
18659     onTriggerClick : function(e)
18660     {
18661         Roo.log('trigger click');
18662         
18663         if(this.disabled || !this.triggerList){
18664             return;
18665         }
18666         
18667         this.page = 0;
18668         this.loadNext = false;
18669         
18670         if(this.isExpanded()){
18671             this.collapse();
18672             if (!this.blockFocus) {
18673                 this.inputEl().focus();
18674             }
18675             
18676         }else {
18677             this.hasFocus = true;
18678             if(this.triggerAction == 'all') {
18679                 this.doQuery(this.allQuery, true);
18680             } else {
18681                 this.doQuery(this.getRawValue());
18682             }
18683             if (!this.blockFocus) {
18684                 this.inputEl().focus();
18685             }
18686         }
18687     },
18688     
18689     onTickableTriggerClick : function(e)
18690     {
18691         if(this.disabled){
18692             return;
18693         }
18694         
18695         this.page = 0;
18696         this.loadNext = false;
18697         this.hasFocus = true;
18698         
18699         if(this.triggerAction == 'all') {
18700             this.doQuery(this.allQuery, true);
18701         } else {
18702             this.doQuery(this.getRawValue());
18703         }
18704     },
18705     
18706     onSearchFieldClick : function(e)
18707     {
18708         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18709             this.onTickableFooterButtonClick(e, false, false);
18710             return;
18711         }
18712         
18713         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18714             return;
18715         }
18716         
18717         this.page = 0;
18718         this.loadNext = false;
18719         this.hasFocus = true;
18720         
18721         if(this.triggerAction == 'all') {
18722             this.doQuery(this.allQuery, true);
18723         } else {
18724             this.doQuery(this.getRawValue());
18725         }
18726     },
18727     
18728     listKeyPress : function(e)
18729     {
18730         //Roo.log('listkeypress');
18731         // scroll to first matching element based on key pres..
18732         if (e.isSpecialKey()) {
18733             return false;
18734         }
18735         var k = String.fromCharCode(e.getKey()).toUpperCase();
18736         //Roo.log(k);
18737         var match  = false;
18738         var csel = this.view.getSelectedNodes();
18739         var cselitem = false;
18740         if (csel.length) {
18741             var ix = this.view.indexOf(csel[0]);
18742             cselitem  = this.store.getAt(ix);
18743             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18744                 cselitem = false;
18745             }
18746             
18747         }
18748         
18749         this.store.each(function(v) { 
18750             if (cselitem) {
18751                 // start at existing selection.
18752                 if (cselitem.id == v.id) {
18753                     cselitem = false;
18754                 }
18755                 return true;
18756             }
18757                 
18758             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18759                 match = this.store.indexOf(v);
18760                 return false;
18761             }
18762             return true;
18763         }, this);
18764         
18765         if (match === false) {
18766             return true; // no more action?
18767         }
18768         // scroll to?
18769         this.view.select(match);
18770         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18771         sn.scrollIntoView(sn.dom.parentNode, false);
18772     },
18773     
18774     onViewScroll : function(e, t){
18775         
18776         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
18777             return;
18778         }
18779         
18780         this.hasQuery = true;
18781         
18782         this.loading = this.list.select('.loading', true).first();
18783         
18784         if(this.loading === null){
18785             this.list.createChild({
18786                 tag: 'div',
18787                 cls: 'loading roo-select2-more-results roo-select2-active',
18788                 html: 'Loading more results...'
18789             });
18790             
18791             this.loading = this.list.select('.loading', true).first();
18792             
18793             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18794             
18795             this.loading.hide();
18796         }
18797         
18798         this.loading.show();
18799         
18800         var _combo = this;
18801         
18802         this.page++;
18803         this.loadNext = true;
18804         
18805         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18806         
18807         return;
18808     },
18809     
18810     addItem : function(o)
18811     {   
18812         var dv = ''; // display value
18813         
18814         if (this.displayField) {
18815             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18816         } else {
18817             // this is an error condition!!!
18818             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18819         }
18820         
18821         if(!dv.length){
18822             return;
18823         }
18824         
18825         var choice = this.choices.createChild({
18826             tag: 'li',
18827             cls: 'roo-select2-search-choice',
18828             cn: [
18829                 {
18830                     tag: 'div',
18831                     html: dv
18832                 },
18833                 {
18834                     tag: 'a',
18835                     href: '#',
18836                     cls: 'roo-select2-search-choice-close fa fa-times',
18837                     tabindex: '-1'
18838                 }
18839             ]
18840             
18841         }, this.searchField);
18842         
18843         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18844         
18845         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18846         
18847         this.item.push(o);
18848         
18849         this.lastData = o;
18850         
18851         this.syncValue();
18852         
18853         this.inputEl().dom.value = '';
18854         
18855         this.validate();
18856     },
18857     
18858     onRemoveItem : function(e, _self, o)
18859     {
18860         e.preventDefault();
18861         
18862         this.lastItem = Roo.apply([], this.item);
18863         
18864         var index = this.item.indexOf(o.data) * 1;
18865         
18866         if( index < 0){
18867             Roo.log('not this item?!');
18868             return;
18869         }
18870         
18871         this.item.splice(index, 1);
18872         o.item.remove();
18873         
18874         this.syncValue();
18875         
18876         this.fireEvent('remove', this, e);
18877         
18878         this.validate();
18879         
18880     },
18881     
18882     syncValue : function()
18883     {
18884         if(!this.item.length){
18885             this.clearValue();
18886             return;
18887         }
18888             
18889         var value = [];
18890         var _this = this;
18891         Roo.each(this.item, function(i){
18892             if(_this.valueField){
18893                 value.push(i[_this.valueField]);
18894                 return;
18895             }
18896
18897             value.push(i);
18898         });
18899
18900         this.value = value.join(',');
18901
18902         if(this.hiddenField){
18903             this.hiddenField.dom.value = this.value;
18904         }
18905         
18906         this.store.fireEvent("datachanged", this.store);
18907         
18908         this.validate();
18909     },
18910     
18911     clearItem : function()
18912     {
18913         if(!this.multiple){
18914             return;
18915         }
18916         
18917         this.item = [];
18918         
18919         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18920            c.remove();
18921         });
18922         
18923         this.syncValue();
18924         
18925         this.validate();
18926         
18927         if(this.tickable && !Roo.isTouch){
18928             this.view.refresh();
18929         }
18930     },
18931     
18932     inputEl: function ()
18933     {
18934         if(Roo.isIOS && this.useNativeIOS){
18935             return this.el.select('select.roo-ios-select', true).first();
18936         }
18937         
18938         if(Roo.isTouch && this.mobileTouchView){
18939             return this.el.select('input.form-control',true).first();
18940         }
18941         
18942         if(this.tickable){
18943             return this.searchField;
18944         }
18945         
18946         return this.el.select('input.form-control',true).first();
18947     },
18948     
18949     onTickableFooterButtonClick : function(e, btn, el)
18950     {
18951         e.preventDefault();
18952         
18953         this.lastItem = Roo.apply([], this.item);
18954         
18955         if(btn && btn.name == 'cancel'){
18956             this.tickItems = Roo.apply([], this.item);
18957             this.collapse();
18958             return;
18959         }
18960         
18961         this.clearItem();
18962         
18963         var _this = this;
18964         
18965         Roo.each(this.tickItems, function(o){
18966             _this.addItem(o);
18967         });
18968         
18969         this.collapse();
18970         
18971     },
18972     
18973     validate : function()
18974     {
18975         if(this.getVisibilityEl().hasClass('hidden')){
18976             return true;
18977         }
18978         
18979         var v = this.getRawValue();
18980         
18981         if(this.multiple){
18982             v = this.getValue();
18983         }
18984         
18985         if(this.disabled || this.allowBlank || v.length){
18986             this.markValid();
18987             return true;
18988         }
18989         
18990         this.markInvalid();
18991         return false;
18992     },
18993     
18994     tickableInputEl : function()
18995     {
18996         if(!this.tickable || !this.editable){
18997             return this.inputEl();
18998         }
18999         
19000         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19001     },
19002     
19003     
19004     getAutoCreateTouchView : function()
19005     {
19006         var id = Roo.id();
19007         
19008         var cfg = {
19009             cls: 'form-group' //input-group
19010         };
19011         
19012         var input =  {
19013             tag: 'input',
19014             id : id,
19015             type : this.inputType,
19016             cls : 'form-control x-combo-noedit',
19017             autocomplete: 'new-password',
19018             placeholder : this.placeholder || '',
19019             readonly : true
19020         };
19021         
19022         if (this.name) {
19023             input.name = this.name;
19024         }
19025         
19026         if (this.size) {
19027             input.cls += ' input-' + this.size;
19028         }
19029         
19030         if (this.disabled) {
19031             input.disabled = true;
19032         }
19033         
19034         var inputblock = {
19035             cls : 'roo-combobox-wrap',
19036             cn : [
19037                 input
19038             ]
19039         };
19040         
19041         if(this.before){
19042             inputblock.cls += ' input-group';
19043             
19044             inputblock.cn.unshift({
19045                 tag :'span',
19046                 cls : 'input-group-addon input-group-prepend input-group-text',
19047                 html : this.before
19048             });
19049         }
19050         
19051         if(this.removable && !this.multiple){
19052             inputblock.cls += ' roo-removable';
19053             
19054             inputblock.cn.push({
19055                 tag: 'button',
19056                 html : 'x',
19057                 cls : 'roo-combo-removable-btn close'
19058             });
19059         }
19060
19061         if(this.hasFeedback && !this.allowBlank){
19062             
19063             inputblock.cls += ' has-feedback';
19064             
19065             inputblock.cn.push({
19066                 tag: 'span',
19067                 cls: 'glyphicon form-control-feedback'
19068             });
19069             
19070         }
19071         
19072         if (this.after) {
19073             
19074             inputblock.cls += (this.before) ? '' : ' input-group';
19075             
19076             inputblock.cn.push({
19077                 tag :'span',
19078                 cls : 'input-group-addon input-group-append input-group-text',
19079                 html : this.after
19080             });
19081         }
19082
19083         
19084         var ibwrap = inputblock;
19085         
19086         if(this.multiple){
19087             ibwrap = {
19088                 tag: 'ul',
19089                 cls: 'roo-select2-choices',
19090                 cn:[
19091                     {
19092                         tag: 'li',
19093                         cls: 'roo-select2-search-field',
19094                         cn: [
19095
19096                             inputblock
19097                         ]
19098                     }
19099                 ]
19100             };
19101         
19102             
19103         }
19104         
19105         var combobox = {
19106             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19107             cn: [
19108                 {
19109                     tag: 'input',
19110                     type : 'hidden',
19111                     cls: 'form-hidden-field'
19112                 },
19113                 ibwrap
19114             ]
19115         };
19116         
19117         if(!this.multiple && this.showToggleBtn){
19118             
19119             var caret = {
19120                 cls: 'caret'
19121             };
19122             
19123             if (this.caret != false) {
19124                 caret = {
19125                      tag: 'i',
19126                      cls: 'fa fa-' + this.caret
19127                 };
19128                 
19129             }
19130             
19131             combobox.cn.push({
19132                 tag :'span',
19133                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19134                 cn : [
19135                     Roo.bootstrap.version == 3 ? caret : '',
19136                     {
19137                         tag: 'span',
19138                         cls: 'combobox-clear',
19139                         cn  : [
19140                             {
19141                                 tag : 'i',
19142                                 cls: 'icon-remove'
19143                             }
19144                         ]
19145                     }
19146                 ]
19147
19148             })
19149         }
19150         
19151         if(this.multiple){
19152             combobox.cls += ' roo-select2-container-multi';
19153         }
19154         
19155         var required =  this.allowBlank ?  {
19156                     tag : 'i',
19157                     style: 'display: none'
19158                 } : {
19159                    tag : 'i',
19160                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19161                    tooltip : 'This field is required'
19162                 };
19163         
19164         var align = this.labelAlign || this.parentLabelAlign();
19165         
19166         if (align ==='left' && this.fieldLabel.length) {
19167
19168             cfg.cn = [
19169                 required,
19170                 {
19171                     tag: 'label',
19172                     cls : 'control-label col-form-label',
19173                     html : this.fieldLabel
19174
19175                 },
19176                 {
19177                     cls : 'roo-combobox-wrap ', 
19178                     cn: [
19179                         combobox
19180                     ]
19181                 }
19182             ];
19183             
19184             var labelCfg = cfg.cn[1];
19185             var contentCfg = cfg.cn[2];
19186             
19187
19188             if(this.indicatorpos == 'right'){
19189                 cfg.cn = [
19190                     {
19191                         tag: 'label',
19192                         'for' :  id,
19193                         cls : 'control-label col-form-label',
19194                         cn : [
19195                             {
19196                                 tag : 'span',
19197                                 html : this.fieldLabel
19198                             },
19199                             required
19200                         ]
19201                     },
19202                     {
19203                         cls : "roo-combobox-wrap ",
19204                         cn: [
19205                             combobox
19206                         ]
19207                     }
19208
19209                 ];
19210                 
19211                 labelCfg = cfg.cn[0];
19212                 contentCfg = cfg.cn[1];
19213             }
19214             
19215            
19216             
19217             if(this.labelWidth > 12){
19218                 labelCfg.style = "width: " + this.labelWidth + 'px';
19219             }
19220            
19221             if(this.labelWidth < 13 && this.labelmd == 0){
19222                 this.labelmd = this.labelWidth;
19223             }
19224             
19225             if(this.labellg > 0){
19226                 labelCfg.cls += ' col-lg-' + this.labellg;
19227                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19228             }
19229             
19230             if(this.labelmd > 0){
19231                 labelCfg.cls += ' col-md-' + this.labelmd;
19232                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19233             }
19234             
19235             if(this.labelsm > 0){
19236                 labelCfg.cls += ' col-sm-' + this.labelsm;
19237                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19238             }
19239             
19240             if(this.labelxs > 0){
19241                 labelCfg.cls += ' col-xs-' + this.labelxs;
19242                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19243             }
19244                 
19245                 
19246         } else if ( this.fieldLabel.length) {
19247             cfg.cn = [
19248                required,
19249                 {
19250                     tag: 'label',
19251                     cls : 'control-label',
19252                     html : this.fieldLabel
19253
19254                 },
19255                 {
19256                     cls : '', 
19257                     cn: [
19258                         combobox
19259                     ]
19260                 }
19261             ];
19262             
19263             if(this.indicatorpos == 'right'){
19264                 cfg.cn = [
19265                     {
19266                         tag: 'label',
19267                         cls : 'control-label',
19268                         html : this.fieldLabel,
19269                         cn : [
19270                             required
19271                         ]
19272                     },
19273                     {
19274                         cls : '', 
19275                         cn: [
19276                             combobox
19277                         ]
19278                     }
19279                 ];
19280             }
19281         } else {
19282             cfg.cn = combobox;    
19283         }
19284         
19285         
19286         var settings = this;
19287         
19288         ['xs','sm','md','lg'].map(function(size){
19289             if (settings[size]) {
19290                 cfg.cls += ' col-' + size + '-' + settings[size];
19291             }
19292         });
19293         
19294         return cfg;
19295     },
19296     
19297     initTouchView : function()
19298     {
19299         this.renderTouchView();
19300         
19301         this.touchViewEl.on('scroll', function(){
19302             this.el.dom.scrollTop = 0;
19303         }, this);
19304         
19305         this.originalValue = this.getValue();
19306         
19307         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19308         
19309         this.inputEl().on("click", this.showTouchView, this);
19310         if (this.triggerEl) {
19311             this.triggerEl.on("click", this.showTouchView, this);
19312         }
19313         
19314         
19315         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19316         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19317         
19318         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19319         
19320         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19321         this.store.on('load', this.onTouchViewLoad, this);
19322         this.store.on('loadexception', this.onTouchViewLoadException, this);
19323         
19324         if(this.hiddenName){
19325             
19326             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19327             
19328             this.hiddenField.dom.value =
19329                 this.hiddenValue !== undefined ? this.hiddenValue :
19330                 this.value !== undefined ? this.value : '';
19331         
19332             this.el.dom.removeAttribute('name');
19333             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19334         }
19335         
19336         if(this.multiple){
19337             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19338             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19339         }
19340         
19341         if(this.removable && !this.multiple){
19342             var close = this.closeTriggerEl();
19343             if(close){
19344                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19345                 close.on('click', this.removeBtnClick, this, close);
19346             }
19347         }
19348         /*
19349          * fix the bug in Safari iOS8
19350          */
19351         this.inputEl().on("focus", function(e){
19352             document.activeElement.blur();
19353         }, this);
19354         
19355         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19356         
19357         return;
19358         
19359         
19360     },
19361     
19362     renderTouchView : function()
19363     {
19364         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19365         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19366         
19367         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19368         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19369         
19370         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19371         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19372         this.touchViewBodyEl.setStyle('overflow', 'auto');
19373         
19374         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19375         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19376         
19377         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19378         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19379         
19380     },
19381     
19382     showTouchView : function()
19383     {
19384         if(this.disabled){
19385             return;
19386         }
19387         
19388         this.touchViewHeaderEl.hide();
19389
19390         if(this.modalTitle.length){
19391             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19392             this.touchViewHeaderEl.show();
19393         }
19394
19395         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19396         this.touchViewEl.show();
19397
19398         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19399         
19400         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19401         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19402
19403         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19404
19405         if(this.modalTitle.length){
19406             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19407         }
19408         
19409         this.touchViewBodyEl.setHeight(bodyHeight);
19410
19411         if(this.animate){
19412             var _this = this;
19413             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19414         }else{
19415             this.touchViewEl.addClass(['in','show']);
19416         }
19417         
19418         if(this._touchViewMask){
19419             Roo.get(document.body).addClass("x-body-masked");
19420             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19421             this._touchViewMask.setStyle('z-index', 10000);
19422             this._touchViewMask.addClass('show');
19423         }
19424         
19425         this.doTouchViewQuery();
19426         
19427     },
19428     
19429     hideTouchView : function()
19430     {
19431         this.touchViewEl.removeClass(['in','show']);
19432
19433         if(this.animate){
19434             var _this = this;
19435             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19436         }else{
19437             this.touchViewEl.setStyle('display', 'none');
19438         }
19439         
19440         if(this._touchViewMask){
19441             this._touchViewMask.removeClass('show');
19442             Roo.get(document.body).removeClass("x-body-masked");
19443         }
19444     },
19445     
19446     setTouchViewValue : function()
19447     {
19448         if(this.multiple){
19449             this.clearItem();
19450         
19451             var _this = this;
19452
19453             Roo.each(this.tickItems, function(o){
19454                 this.addItem(o);
19455             }, this);
19456         }
19457         
19458         this.hideTouchView();
19459     },
19460     
19461     doTouchViewQuery : function()
19462     {
19463         var qe = {
19464             query: '',
19465             forceAll: true,
19466             combo: this,
19467             cancel:false
19468         };
19469         
19470         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19471             return false;
19472         }
19473         
19474         if(!this.alwaysQuery || this.mode == 'local'){
19475             this.onTouchViewLoad();
19476             return;
19477         }
19478         
19479         this.store.load();
19480     },
19481     
19482     onTouchViewBeforeLoad : function(combo,opts)
19483     {
19484         return;
19485     },
19486
19487     // private
19488     onTouchViewLoad : function()
19489     {
19490         if(this.store.getCount() < 1){
19491             this.onTouchViewEmptyResults();
19492             return;
19493         }
19494         
19495         this.clearTouchView();
19496         
19497         var rawValue = this.getRawValue();
19498         
19499         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19500         
19501         this.tickItems = [];
19502         
19503         this.store.data.each(function(d, rowIndex){
19504             var row = this.touchViewListGroup.createChild(template);
19505             
19506             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19507                 row.addClass(d.data.cls);
19508             }
19509             
19510             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19511                 var cfg = {
19512                     data : d.data,
19513                     html : d.data[this.displayField]
19514                 };
19515                 
19516                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19517                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19518                 }
19519             }
19520             row.removeClass('selected');
19521             if(!this.multiple && this.valueField &&
19522                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19523             {
19524                 // radio buttons..
19525                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19526                 row.addClass('selected');
19527             }
19528             
19529             if(this.multiple && this.valueField &&
19530                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19531             {
19532                 
19533                 // checkboxes...
19534                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19535                 this.tickItems.push(d.data);
19536             }
19537             
19538             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19539             
19540         }, this);
19541         
19542         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19543         
19544         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19545
19546         if(this.modalTitle.length){
19547             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19548         }
19549
19550         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19551         
19552         if(this.mobile_restrict_height && listHeight < bodyHeight){
19553             this.touchViewBodyEl.setHeight(listHeight);
19554         }
19555         
19556         var _this = this;
19557         
19558         if(firstChecked && listHeight > bodyHeight){
19559             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19560         }
19561         
19562     },
19563     
19564     onTouchViewLoadException : function()
19565     {
19566         this.hideTouchView();
19567     },
19568     
19569     onTouchViewEmptyResults : function()
19570     {
19571         this.clearTouchView();
19572         
19573         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19574         
19575         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19576         
19577     },
19578     
19579     clearTouchView : function()
19580     {
19581         this.touchViewListGroup.dom.innerHTML = '';
19582     },
19583     
19584     onTouchViewClick : function(e, el, o)
19585     {
19586         e.preventDefault();
19587         
19588         var row = o.row;
19589         var rowIndex = o.rowIndex;
19590         
19591         var r = this.store.getAt(rowIndex);
19592         
19593         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19594             
19595             if(!this.multiple){
19596                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19597                     c.dom.removeAttribute('checked');
19598                 }, this);
19599
19600                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19601
19602                 this.setFromData(r.data);
19603
19604                 var close = this.closeTriggerEl();
19605
19606                 if(close){
19607                     close.show();
19608                 }
19609
19610                 this.hideTouchView();
19611
19612                 this.fireEvent('select', this, r, rowIndex);
19613
19614                 return;
19615             }
19616
19617             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19618                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19619                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19620                 return;
19621             }
19622
19623             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19624             this.addItem(r.data);
19625             this.tickItems.push(r.data);
19626         }
19627     },
19628     
19629     getAutoCreateNativeIOS : function()
19630     {
19631         var cfg = {
19632             cls: 'form-group' //input-group,
19633         };
19634         
19635         var combobox =  {
19636             tag: 'select',
19637             cls : 'roo-ios-select'
19638         };
19639         
19640         if (this.name) {
19641             combobox.name = this.name;
19642         }
19643         
19644         if (this.disabled) {
19645             combobox.disabled = true;
19646         }
19647         
19648         var settings = this;
19649         
19650         ['xs','sm','md','lg'].map(function(size){
19651             if (settings[size]) {
19652                 cfg.cls += ' col-' + size + '-' + settings[size];
19653             }
19654         });
19655         
19656         cfg.cn = combobox;
19657         
19658         return cfg;
19659         
19660     },
19661     
19662     initIOSView : function()
19663     {
19664         this.store.on('load', this.onIOSViewLoad, this);
19665         
19666         return;
19667     },
19668     
19669     onIOSViewLoad : function()
19670     {
19671         if(this.store.getCount() < 1){
19672             return;
19673         }
19674         
19675         this.clearIOSView();
19676         
19677         if(this.allowBlank) {
19678             
19679             var default_text = '-- SELECT --';
19680             
19681             if(this.placeholder.length){
19682                 default_text = this.placeholder;
19683             }
19684             
19685             if(this.emptyTitle.length){
19686                 default_text += ' - ' + this.emptyTitle + ' -';
19687             }
19688             
19689             var opt = this.inputEl().createChild({
19690                 tag: 'option',
19691                 value : 0,
19692                 html : default_text
19693             });
19694             
19695             var o = {};
19696             o[this.valueField] = 0;
19697             o[this.displayField] = default_text;
19698             
19699             this.ios_options.push({
19700                 data : o,
19701                 el : opt
19702             });
19703             
19704         }
19705         
19706         this.store.data.each(function(d, rowIndex){
19707             
19708             var html = '';
19709             
19710             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19711                 html = d.data[this.displayField];
19712             }
19713             
19714             var value = '';
19715             
19716             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19717                 value = d.data[this.valueField];
19718             }
19719             
19720             var option = {
19721                 tag: 'option',
19722                 value : value,
19723                 html : html
19724             };
19725             
19726             if(this.value == d.data[this.valueField]){
19727                 option['selected'] = true;
19728             }
19729             
19730             var opt = this.inputEl().createChild(option);
19731             
19732             this.ios_options.push({
19733                 data : d.data,
19734                 el : opt
19735             });
19736             
19737         }, this);
19738         
19739         this.inputEl().on('change', function(){
19740            this.fireEvent('select', this);
19741         }, this);
19742         
19743     },
19744     
19745     clearIOSView: function()
19746     {
19747         this.inputEl().dom.innerHTML = '';
19748         
19749         this.ios_options = [];
19750     },
19751     
19752     setIOSValue: function(v)
19753     {
19754         this.value = v;
19755         
19756         if(!this.ios_options){
19757             return;
19758         }
19759         
19760         Roo.each(this.ios_options, function(opts){
19761            
19762            opts.el.dom.removeAttribute('selected');
19763            
19764            if(opts.data[this.valueField] != v){
19765                return;
19766            }
19767            
19768            opts.el.dom.setAttribute('selected', true);
19769            
19770         }, this);
19771     }
19772
19773     /** 
19774     * @cfg {Boolean} grow 
19775     * @hide 
19776     */
19777     /** 
19778     * @cfg {Number} growMin 
19779     * @hide 
19780     */
19781     /** 
19782     * @cfg {Number} growMax 
19783     * @hide 
19784     */
19785     /**
19786      * @hide
19787      * @method autoSize
19788      */
19789 });
19790
19791 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19792     
19793     header : {
19794         tag: 'div',
19795         cls: 'modal-header',
19796         cn: [
19797             {
19798                 tag: 'h4',
19799                 cls: 'modal-title'
19800             }
19801         ]
19802     },
19803     
19804     body : {
19805         tag: 'div',
19806         cls: 'modal-body',
19807         cn: [
19808             {
19809                 tag: 'ul',
19810                 cls: 'list-group'
19811             }
19812         ]
19813     },
19814     
19815     listItemRadio : {
19816         tag: 'li',
19817         cls: 'list-group-item',
19818         cn: [
19819             {
19820                 tag: 'span',
19821                 cls: 'roo-combobox-list-group-item-value'
19822             },
19823             {
19824                 tag: 'div',
19825                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19826                 cn: [
19827                     {
19828                         tag: 'input',
19829                         type: 'radio'
19830                     },
19831                     {
19832                         tag: 'label'
19833                     }
19834                 ]
19835             }
19836         ]
19837     },
19838     
19839     listItemCheckbox : {
19840         tag: 'li',
19841         cls: 'list-group-item',
19842         cn: [
19843             {
19844                 tag: 'span',
19845                 cls: 'roo-combobox-list-group-item-value'
19846             },
19847             {
19848                 tag: 'div',
19849                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19850                 cn: [
19851                     {
19852                         tag: 'input',
19853                         type: 'checkbox'
19854                     },
19855                     {
19856                         tag: 'label'
19857                     }
19858                 ]
19859             }
19860         ]
19861     },
19862     
19863     emptyResult : {
19864         tag: 'div',
19865         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19866     },
19867     
19868     footer : {
19869         tag: 'div',
19870         cls: 'modal-footer',
19871         cn: [
19872             {
19873                 tag: 'div',
19874                 cls: 'row',
19875                 cn: [
19876                     {
19877                         tag: 'div',
19878                         cls: 'col-xs-6 text-left',
19879                         cn: {
19880                             tag: 'button',
19881                             cls: 'btn btn-danger roo-touch-view-cancel',
19882                             html: 'Cancel'
19883                         }
19884                     },
19885                     {
19886                         tag: 'div',
19887                         cls: 'col-xs-6 text-right',
19888                         cn: {
19889                             tag: 'button',
19890                             cls: 'btn btn-success roo-touch-view-ok',
19891                             html: 'OK'
19892                         }
19893                     }
19894                 ]
19895             }
19896         ]
19897         
19898     }
19899 });
19900
19901 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19902     
19903     touchViewTemplate : {
19904         tag: 'div',
19905         cls: 'modal fade roo-combobox-touch-view',
19906         cn: [
19907             {
19908                 tag: 'div',
19909                 cls: 'modal-dialog',
19910                 style : 'position:fixed', // we have to fix position....
19911                 cn: [
19912                     {
19913                         tag: 'div',
19914                         cls: 'modal-content',
19915                         cn: [
19916                             Roo.bootstrap.form.ComboBox.header,
19917                             Roo.bootstrap.form.ComboBox.body,
19918                             Roo.bootstrap.form.ComboBox.footer
19919                         ]
19920                     }
19921                 ]
19922             }
19923         ]
19924     }
19925 });/*
19926  * Based on:
19927  * Ext JS Library 1.1.1
19928  * Copyright(c) 2006-2007, Ext JS, LLC.
19929  *
19930  * Originally Released Under LGPL - original licence link has changed is not relivant.
19931  *
19932  * Fork - LGPL
19933  * <script type="text/javascript">
19934  */
19935
19936 /**
19937  * @class Roo.View
19938  * @extends Roo.util.Observable
19939  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19940  * This class also supports single and multi selection modes. <br>
19941  * Create a data model bound view:
19942  <pre><code>
19943  var store = new Roo.data.Store(...);
19944
19945  var view = new Roo.View({
19946     el : "my-element",
19947     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19948  
19949     singleSelect: true,
19950     selectedClass: "ydataview-selected",
19951     store: store
19952  });
19953
19954  // listen for node click?
19955  view.on("click", function(vw, index, node, e){
19956  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19957  });
19958
19959  // load XML data
19960  dataModel.load("foobar.xml");
19961  </code></pre>
19962  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19963  * <br><br>
19964  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19965  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19966  * 
19967  * Note: old style constructor is still suported (container, template, config)
19968  * 
19969  * @constructor
19970  * Create a new View
19971  * @param {Object} config The config object
19972  * 
19973  */
19974 Roo.View = function(config, depreciated_tpl, depreciated_config){
19975     
19976     this.parent = false;
19977     
19978     if (typeof(depreciated_tpl) == 'undefined') {
19979         // new way.. - universal constructor.
19980         Roo.apply(this, config);
19981         this.el  = Roo.get(this.el);
19982     } else {
19983         // old format..
19984         this.el  = Roo.get(config);
19985         this.tpl = depreciated_tpl;
19986         Roo.apply(this, depreciated_config);
19987     }
19988     this.wrapEl  = this.el.wrap().wrap();
19989     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19990     
19991     
19992     if(typeof(this.tpl) == "string"){
19993         this.tpl = new Roo.Template(this.tpl);
19994     } else {
19995         // support xtype ctors..
19996         this.tpl = new Roo.factory(this.tpl, Roo);
19997     }
19998     
19999     
20000     this.tpl.compile();
20001     
20002     /** @private */
20003     this.addEvents({
20004         /**
20005          * @event beforeclick
20006          * Fires before a click is processed. Returns false to cancel the default action.
20007          * @param {Roo.View} this
20008          * @param {Number} index The index of the target node
20009          * @param {HTMLElement} node The target node
20010          * @param {Roo.EventObject} e The raw event object
20011          */
20012             "beforeclick" : true,
20013         /**
20014          * @event click
20015          * Fires when a template node is clicked.
20016          * @param {Roo.View} this
20017          * @param {Number} index The index of the target node
20018          * @param {HTMLElement} node The target node
20019          * @param {Roo.EventObject} e The raw event object
20020          */
20021             "click" : true,
20022         /**
20023          * @event dblclick
20024          * Fires when a template node is double clicked.
20025          * @param {Roo.View} this
20026          * @param {Number} index The index of the target node
20027          * @param {HTMLElement} node The target node
20028          * @param {Roo.EventObject} e The raw event object
20029          */
20030             "dblclick" : true,
20031         /**
20032          * @event contextmenu
20033          * Fires when a template node is right clicked.
20034          * @param {Roo.View} this
20035          * @param {Number} index The index of the target node
20036          * @param {HTMLElement} node The target node
20037          * @param {Roo.EventObject} e The raw event object
20038          */
20039             "contextmenu" : true,
20040         /**
20041          * @event selectionchange
20042          * Fires when the selected nodes change.
20043          * @param {Roo.View} this
20044          * @param {Array} selections Array of the selected nodes
20045          */
20046             "selectionchange" : true,
20047     
20048         /**
20049          * @event beforeselect
20050          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20051          * @param {Roo.View} this
20052          * @param {HTMLElement} node The node to be selected
20053          * @param {Array} selections Array of currently selected nodes
20054          */
20055             "beforeselect" : true,
20056         /**
20057          * @event preparedata
20058          * Fires on every row to render, to allow you to change the data.
20059          * @param {Roo.View} this
20060          * @param {Object} data to be rendered (change this)
20061          */
20062           "preparedata" : true
20063           
20064           
20065         });
20066
20067
20068
20069     this.el.on({
20070         "click": this.onClick,
20071         "dblclick": this.onDblClick,
20072         "contextmenu": this.onContextMenu,
20073         scope:this
20074     });
20075
20076     this.selections = [];
20077     this.nodes = [];
20078     this.cmp = new Roo.CompositeElementLite([]);
20079     if(this.store){
20080         this.store = Roo.factory(this.store, Roo.data);
20081         this.setStore(this.store, true);
20082     }
20083     
20084     if ( this.footer && this.footer.xtype) {
20085            
20086          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20087         
20088         this.footer.dataSource = this.store;
20089         this.footer.container = fctr;
20090         this.footer = Roo.factory(this.footer, Roo);
20091         fctr.insertFirst(this.el);
20092         
20093         // this is a bit insane - as the paging toolbar seems to detach the el..
20094 //        dom.parentNode.parentNode.parentNode
20095          // they get detached?
20096     }
20097     
20098     
20099     Roo.View.superclass.constructor.call(this);
20100     
20101     
20102 };
20103
20104 Roo.extend(Roo.View, Roo.util.Observable, {
20105     
20106      /**
20107      * @cfg {Roo.data.Store} store Data store to load data from.
20108      */
20109     store : false,
20110     
20111     /**
20112      * @cfg {String|Roo.Element} el The container element.
20113      */
20114     el : '',
20115     
20116     /**
20117      * @cfg {String|Roo.Template} tpl The template used by this View 
20118      */
20119     tpl : false,
20120     /**
20121      * @cfg {String} dataName the named area of the template to use as the data area
20122      *                          Works with domtemplates roo-name="name"
20123      */
20124     dataName: false,
20125     /**
20126      * @cfg {String} selectedClass The css class to add to selected nodes
20127      */
20128     selectedClass : "x-view-selected",
20129      /**
20130      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20131      */
20132     emptyText : "",
20133     
20134     /**
20135      * @cfg {String} text to display on mask (default Loading)
20136      */
20137     mask : false,
20138     /**
20139      * @cfg {Boolean} multiSelect Allow multiple selection
20140      */
20141     multiSelect : false,
20142     /**
20143      * @cfg {Boolean} singleSelect Allow single selection
20144      */
20145     singleSelect:  false,
20146     
20147     /**
20148      * @cfg {Boolean} toggleSelect - selecting 
20149      */
20150     toggleSelect : false,
20151     
20152     /**
20153      * @cfg {Boolean} tickable - selecting 
20154      */
20155     tickable : false,
20156     
20157     /**
20158      * Returns the element this view is bound to.
20159      * @return {Roo.Element}
20160      */
20161     getEl : function(){
20162         return this.wrapEl;
20163     },
20164     
20165     
20166
20167     /**
20168      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20169      */
20170     refresh : function(){
20171         //Roo.log('refresh');
20172         var t = this.tpl;
20173         
20174         // if we are using something like 'domtemplate', then
20175         // the what gets used is:
20176         // t.applySubtemplate(NAME, data, wrapping data..)
20177         // the outer template then get' applied with
20178         //     the store 'extra data'
20179         // and the body get's added to the
20180         //      roo-name="data" node?
20181         //      <span class='roo-tpl-{name}'></span> ?????
20182         
20183         
20184         
20185         this.clearSelections();
20186         this.el.update("");
20187         var html = [];
20188         var records = this.store.getRange();
20189         if(records.length < 1) {
20190             
20191             // is this valid??  = should it render a template??
20192             
20193             this.el.update(this.emptyText);
20194             return;
20195         }
20196         var el = this.el;
20197         if (this.dataName) {
20198             this.el.update(t.apply(this.store.meta)); //????
20199             el = this.el.child('.roo-tpl-' + this.dataName);
20200         }
20201         
20202         for(var i = 0, len = records.length; i < len; i++){
20203             var data = this.prepareData(records[i].data, i, records[i]);
20204             this.fireEvent("preparedata", this, data, i, records[i]);
20205             
20206             var d = Roo.apply({}, data);
20207             
20208             if(this.tickable){
20209                 Roo.apply(d, {'roo-id' : Roo.id()});
20210                 
20211                 var _this = this;
20212             
20213                 Roo.each(this.parent.item, function(item){
20214                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20215                         return;
20216                     }
20217                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20218                 });
20219             }
20220             
20221             html[html.length] = Roo.util.Format.trim(
20222                 this.dataName ?
20223                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20224                     t.apply(d)
20225             );
20226         }
20227         
20228         
20229         
20230         el.update(html.join(""));
20231         this.nodes = el.dom.childNodes;
20232         this.updateIndexes(0);
20233     },
20234     
20235
20236     /**
20237      * Function to override to reformat the data that is sent to
20238      * the template for each node.
20239      * DEPRICATED - use the preparedata event handler.
20240      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20241      * a JSON object for an UpdateManager bound view).
20242      */
20243     prepareData : function(data, index, record)
20244     {
20245         this.fireEvent("preparedata", this, data, index, record);
20246         return data;
20247     },
20248
20249     onUpdate : function(ds, record){
20250         // Roo.log('on update');   
20251         this.clearSelections();
20252         var index = this.store.indexOf(record);
20253         var n = this.nodes[index];
20254         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20255         n.parentNode.removeChild(n);
20256         this.updateIndexes(index, index);
20257     },
20258
20259     
20260     
20261 // --------- FIXME     
20262     onAdd : function(ds, records, index)
20263     {
20264         //Roo.log(['on Add', ds, records, index] );        
20265         this.clearSelections();
20266         if(this.nodes.length == 0){
20267             this.refresh();
20268             return;
20269         }
20270         var n = this.nodes[index];
20271         for(var i = 0, len = records.length; i < len; i++){
20272             var d = this.prepareData(records[i].data, i, records[i]);
20273             if(n){
20274                 this.tpl.insertBefore(n, d);
20275             }else{
20276                 
20277                 this.tpl.append(this.el, d);
20278             }
20279         }
20280         this.updateIndexes(index);
20281     },
20282
20283     onRemove : function(ds, record, index){
20284        // Roo.log('onRemove');
20285         this.clearSelections();
20286         var el = this.dataName  ?
20287             this.el.child('.roo-tpl-' + this.dataName) :
20288             this.el; 
20289         
20290         el.dom.removeChild(this.nodes[index]);
20291         this.updateIndexes(index);
20292     },
20293
20294     /**
20295      * Refresh an individual node.
20296      * @param {Number} index
20297      */
20298     refreshNode : function(index){
20299         this.onUpdate(this.store, this.store.getAt(index));
20300     },
20301
20302     updateIndexes : function(startIndex, endIndex){
20303         var ns = this.nodes;
20304         startIndex = startIndex || 0;
20305         endIndex = endIndex || ns.length - 1;
20306         for(var i = startIndex; i <= endIndex; i++){
20307             ns[i].nodeIndex = i;
20308         }
20309     },
20310
20311     /**
20312      * Changes the data store this view uses and refresh the view.
20313      * @param {Store} store
20314      */
20315     setStore : function(store, initial){
20316         if(!initial && this.store){
20317             this.store.un("datachanged", this.refresh);
20318             this.store.un("add", this.onAdd);
20319             this.store.un("remove", this.onRemove);
20320             this.store.un("update", this.onUpdate);
20321             this.store.un("clear", this.refresh);
20322             this.store.un("beforeload", this.onBeforeLoad);
20323             this.store.un("load", this.onLoad);
20324             this.store.un("loadexception", this.onLoad);
20325         }
20326         if(store){
20327           
20328             store.on("datachanged", this.refresh, this);
20329             store.on("add", this.onAdd, this);
20330             store.on("remove", this.onRemove, this);
20331             store.on("update", this.onUpdate, this);
20332             store.on("clear", this.refresh, this);
20333             store.on("beforeload", this.onBeforeLoad, this);
20334             store.on("load", this.onLoad, this);
20335             store.on("loadexception", this.onLoad, this);
20336         }
20337         
20338         if(store){
20339             this.refresh();
20340         }
20341     },
20342     /**
20343      * onbeforeLoad - masks the loading area.
20344      *
20345      */
20346     onBeforeLoad : function(store,opts)
20347     {
20348          //Roo.log('onBeforeLoad');   
20349         if (!opts.add) {
20350             this.el.update("");
20351         }
20352         this.el.mask(this.mask ? this.mask : "Loading" ); 
20353     },
20354     onLoad : function ()
20355     {
20356         this.el.unmask();
20357     },
20358     
20359
20360     /**
20361      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20362      * @param {HTMLElement} node
20363      * @return {HTMLElement} The template node
20364      */
20365     findItemFromChild : function(node){
20366         var el = this.dataName  ?
20367             this.el.child('.roo-tpl-' + this.dataName,true) :
20368             this.el.dom; 
20369         
20370         if(!node || node.parentNode == el){
20371                     return node;
20372             }
20373             var p = node.parentNode;
20374             while(p && p != el){
20375             if(p.parentNode == el){
20376                 return p;
20377             }
20378             p = p.parentNode;
20379         }
20380             return null;
20381     },
20382
20383     /** @ignore */
20384     onClick : function(e){
20385         var item = this.findItemFromChild(e.getTarget());
20386         if(item){
20387             var index = this.indexOf(item);
20388             if(this.onItemClick(item, index, e) !== false){
20389                 this.fireEvent("click", this, index, item, e);
20390             }
20391         }else{
20392             this.clearSelections();
20393         }
20394     },
20395
20396     /** @ignore */
20397     onContextMenu : function(e){
20398         var item = this.findItemFromChild(e.getTarget());
20399         if(item){
20400             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20401         }
20402     },
20403
20404     /** @ignore */
20405     onDblClick : function(e){
20406         var item = this.findItemFromChild(e.getTarget());
20407         if(item){
20408             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20409         }
20410     },
20411
20412     onItemClick : function(item, index, e)
20413     {
20414         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20415             return false;
20416         }
20417         if (this.toggleSelect) {
20418             var m = this.isSelected(item) ? 'unselect' : 'select';
20419             //Roo.log(m);
20420             var _t = this;
20421             _t[m](item, true, false);
20422             return true;
20423         }
20424         if(this.multiSelect || this.singleSelect){
20425             if(this.multiSelect && e.shiftKey && this.lastSelection){
20426                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20427             }else{
20428                 this.select(item, this.multiSelect && e.ctrlKey);
20429                 this.lastSelection = item;
20430             }
20431             
20432             if(!this.tickable){
20433                 e.preventDefault();
20434             }
20435             
20436         }
20437         return true;
20438     },
20439
20440     /**
20441      * Get the number of selected nodes.
20442      * @return {Number}
20443      */
20444     getSelectionCount : function(){
20445         return this.selections.length;
20446     },
20447
20448     /**
20449      * Get the currently selected nodes.
20450      * @return {Array} An array of HTMLElements
20451      */
20452     getSelectedNodes : function(){
20453         return this.selections;
20454     },
20455
20456     /**
20457      * Get the indexes of the selected nodes.
20458      * @return {Array}
20459      */
20460     getSelectedIndexes : function(){
20461         var indexes = [], s = this.selections;
20462         for(var i = 0, len = s.length; i < len; i++){
20463             indexes.push(s[i].nodeIndex);
20464         }
20465         return indexes;
20466     },
20467
20468     /**
20469      * Clear all selections
20470      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20471      */
20472     clearSelections : function(suppressEvent){
20473         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20474             this.cmp.elements = this.selections;
20475             this.cmp.removeClass(this.selectedClass);
20476             this.selections = [];
20477             if(!suppressEvent){
20478                 this.fireEvent("selectionchange", this, this.selections);
20479             }
20480         }
20481     },
20482
20483     /**
20484      * Returns true if the passed node is selected
20485      * @param {HTMLElement/Number} node The node or node index
20486      * @return {Boolean}
20487      */
20488     isSelected : function(node){
20489         var s = this.selections;
20490         if(s.length < 1){
20491             return false;
20492         }
20493         node = this.getNode(node);
20494         return s.indexOf(node) !== -1;
20495     },
20496
20497     /**
20498      * Selects nodes.
20499      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20500      * @param {Boolean} keepExisting (optional) true to keep existing selections
20501      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20502      */
20503     select : function(nodeInfo, keepExisting, suppressEvent){
20504         if(nodeInfo instanceof Array){
20505             if(!keepExisting){
20506                 this.clearSelections(true);
20507             }
20508             for(var i = 0, len = nodeInfo.length; i < len; i++){
20509                 this.select(nodeInfo[i], true, true);
20510             }
20511             return;
20512         } 
20513         var node = this.getNode(nodeInfo);
20514         if(!node || this.isSelected(node)){
20515             return; // already selected.
20516         }
20517         if(!keepExisting){
20518             this.clearSelections(true);
20519         }
20520         
20521         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20522             Roo.fly(node).addClass(this.selectedClass);
20523             this.selections.push(node);
20524             if(!suppressEvent){
20525                 this.fireEvent("selectionchange", this, this.selections);
20526             }
20527         }
20528         
20529         
20530     },
20531       /**
20532      * Unselects nodes.
20533      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20534      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20536      */
20537     unselect : function(nodeInfo, keepExisting, suppressEvent)
20538     {
20539         if(nodeInfo instanceof Array){
20540             Roo.each(this.selections, function(s) {
20541                 this.unselect(s, nodeInfo);
20542             }, this);
20543             return;
20544         }
20545         var node = this.getNode(nodeInfo);
20546         if(!node || !this.isSelected(node)){
20547             //Roo.log("not selected");
20548             return; // not selected.
20549         }
20550         // fireevent???
20551         var ns = [];
20552         Roo.each(this.selections, function(s) {
20553             if (s == node ) {
20554                 Roo.fly(node).removeClass(this.selectedClass);
20555
20556                 return;
20557             }
20558             ns.push(s);
20559         },this);
20560         
20561         this.selections= ns;
20562         this.fireEvent("selectionchange", this, this.selections);
20563     },
20564
20565     /**
20566      * Gets a template node.
20567      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20568      * @return {HTMLElement} The node or null if it wasn't found
20569      */
20570     getNode : function(nodeInfo){
20571         if(typeof nodeInfo == "string"){
20572             return document.getElementById(nodeInfo);
20573         }else if(typeof nodeInfo == "number"){
20574             return this.nodes[nodeInfo];
20575         }
20576         return nodeInfo;
20577     },
20578
20579     /**
20580      * Gets a range template nodes.
20581      * @param {Number} startIndex
20582      * @param {Number} endIndex
20583      * @return {Array} An array of nodes
20584      */
20585     getNodes : function(start, end){
20586         var ns = this.nodes;
20587         start = start || 0;
20588         end = typeof end == "undefined" ? ns.length - 1 : end;
20589         var nodes = [];
20590         if(start <= end){
20591             for(var i = start; i <= end; i++){
20592                 nodes.push(ns[i]);
20593             }
20594         } else{
20595             for(var i = start; i >= end; i--){
20596                 nodes.push(ns[i]);
20597             }
20598         }
20599         return nodes;
20600     },
20601
20602     /**
20603      * Finds the index of the passed node
20604      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20605      * @return {Number} The index of the node or -1
20606      */
20607     indexOf : function(node){
20608         node = this.getNode(node);
20609         if(typeof node.nodeIndex == "number"){
20610             return node.nodeIndex;
20611         }
20612         var ns = this.nodes;
20613         for(var i = 0, len = ns.length; i < len; i++){
20614             if(ns[i] == node){
20615                 return i;
20616             }
20617         }
20618         return -1;
20619     }
20620 });
20621 /*
20622  * - LGPL
20623  *
20624  * based on jquery fullcalendar
20625  * 
20626  */
20627
20628 Roo.bootstrap = Roo.bootstrap || {};
20629 /**
20630  * @class Roo.bootstrap.Calendar
20631  * @extends Roo.bootstrap.Component
20632  * Bootstrap Calendar class
20633  * @cfg {Boolean} loadMask (true|false) default false
20634  * @cfg {Object} header generate the user specific header of the calendar, default false
20635
20636  * @constructor
20637  * Create a new Container
20638  * @param {Object} config The config object
20639  */
20640
20641
20642
20643 Roo.bootstrap.Calendar = function(config){
20644     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20645      this.addEvents({
20646         /**
20647              * @event select
20648              * Fires when a date is selected
20649              * @param {DatePicker} this
20650              * @param {Date} date The selected date
20651              */
20652         'select': true,
20653         /**
20654              * @event monthchange
20655              * Fires when the displayed month changes 
20656              * @param {DatePicker} this
20657              * @param {Date} date The selected month
20658              */
20659         'monthchange': true,
20660         /**
20661              * @event evententer
20662              * Fires when mouse over an event
20663              * @param {Calendar} this
20664              * @param {event} Event
20665              */
20666         'evententer': true,
20667         /**
20668              * @event eventleave
20669              * Fires when the mouse leaves an
20670              * @param {Calendar} this
20671              * @param {event}
20672              */
20673         'eventleave': true,
20674         /**
20675              * @event eventclick
20676              * Fires when the mouse click an
20677              * @param {Calendar} this
20678              * @param {event}
20679              */
20680         'eventclick': true
20681         
20682     });
20683
20684 };
20685
20686 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20687     
20688           /**
20689      * @cfg {Roo.data.Store} store
20690      * The data source for the calendar
20691      */
20692         store : false,
20693      /**
20694      * @cfg {Number} startDay
20695      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20696      */
20697     startDay : 0,
20698     
20699     loadMask : false,
20700     
20701     header : false,
20702       
20703     getAutoCreate : function(){
20704         
20705         
20706         var fc_button = function(name, corner, style, content ) {
20707             return Roo.apply({},{
20708                 tag : 'span',
20709                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20710                          (corner.length ?
20711                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20712                             ''
20713                         ),
20714                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20715                 unselectable: 'on'
20716             });
20717         };
20718         
20719         var header = {};
20720         
20721         if(!this.header){
20722             header = {
20723                 tag : 'table',
20724                 cls : 'fc-header',
20725                 style : 'width:100%',
20726                 cn : [
20727                     {
20728                         tag: 'tr',
20729                         cn : [
20730                             {
20731                                 tag : 'td',
20732                                 cls : 'fc-header-left',
20733                                 cn : [
20734                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20735                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20736                                     { tag: 'span', cls: 'fc-header-space' },
20737                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20738
20739
20740                                 ]
20741                             },
20742
20743                             {
20744                                 tag : 'td',
20745                                 cls : 'fc-header-center',
20746                                 cn : [
20747                                     {
20748                                         tag: 'span',
20749                                         cls: 'fc-header-title',
20750                                         cn : {
20751                                             tag: 'H2',
20752                                             html : 'month / year'
20753                                         }
20754                                     }
20755
20756                                 ]
20757                             },
20758                             {
20759                                 tag : 'td',
20760                                 cls : 'fc-header-right',
20761                                 cn : [
20762                               /*      fc_button('month', 'left', '', 'month' ),
20763                                     fc_button('week', '', '', 'week' ),
20764                                     fc_button('day', 'right', '', 'day' )
20765                                 */    
20766
20767                                 ]
20768                             }
20769
20770                         ]
20771                     }
20772                 ]
20773             };
20774         }
20775         
20776         header = this.header;
20777         
20778        
20779         var cal_heads = function() {
20780             var ret = [];
20781             // fixme - handle this.
20782             
20783             for (var i =0; i < Date.dayNames.length; i++) {
20784                 var d = Date.dayNames[i];
20785                 ret.push({
20786                     tag: 'th',
20787                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20788                     html : d.substring(0,3)
20789                 });
20790                 
20791             }
20792             ret[0].cls += ' fc-first';
20793             ret[6].cls += ' fc-last';
20794             return ret;
20795         };
20796         var cal_cell = function(n) {
20797             return  {
20798                 tag: 'td',
20799                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20800                 cn : [
20801                     {
20802                         cn : [
20803                             {
20804                                 cls: 'fc-day-number',
20805                                 html: 'D'
20806                             },
20807                             {
20808                                 cls: 'fc-day-content',
20809                              
20810                                 cn : [
20811                                      {
20812                                         style: 'position: relative;' // height: 17px;
20813                                     }
20814                                 ]
20815                             }
20816                             
20817                             
20818                         ]
20819                     }
20820                 ]
20821                 
20822             }
20823         };
20824         var cal_rows = function() {
20825             
20826             var ret = [];
20827             for (var r = 0; r < 6; r++) {
20828                 var row= {
20829                     tag : 'tr',
20830                     cls : 'fc-week',
20831                     cn : []
20832                 };
20833                 
20834                 for (var i =0; i < Date.dayNames.length; i++) {
20835                     var d = Date.dayNames[i];
20836                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20837
20838                 }
20839                 row.cn[0].cls+=' fc-first';
20840                 row.cn[0].cn[0].style = 'min-height:90px';
20841                 row.cn[6].cls+=' fc-last';
20842                 ret.push(row);
20843                 
20844             }
20845             ret[0].cls += ' fc-first';
20846             ret[4].cls += ' fc-prev-last';
20847             ret[5].cls += ' fc-last';
20848             return ret;
20849             
20850         };
20851         
20852         var cal_table = {
20853             tag: 'table',
20854             cls: 'fc-border-separate',
20855             style : 'width:100%',
20856             cellspacing  : 0,
20857             cn : [
20858                 { 
20859                     tag: 'thead',
20860                     cn : [
20861                         { 
20862                             tag: 'tr',
20863                             cls : 'fc-first fc-last',
20864                             cn : cal_heads()
20865                         }
20866                     ]
20867                 },
20868                 { 
20869                     tag: 'tbody',
20870                     cn : cal_rows()
20871                 }
20872                   
20873             ]
20874         };
20875          
20876          var cfg = {
20877             cls : 'fc fc-ltr',
20878             cn : [
20879                 header,
20880                 {
20881                     cls : 'fc-content',
20882                     style : "position: relative;",
20883                     cn : [
20884                         {
20885                             cls : 'fc-view fc-view-month fc-grid',
20886                             style : 'position: relative',
20887                             unselectable : 'on',
20888                             cn : [
20889                                 {
20890                                     cls : 'fc-event-container',
20891                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20892                                 },
20893                                 cal_table
20894                             ]
20895                         }
20896                     ]
20897     
20898                 }
20899            ] 
20900             
20901         };
20902         
20903          
20904         
20905         return cfg;
20906     },
20907     
20908     
20909     initEvents : function()
20910     {
20911         if(!this.store){
20912             throw "can not find store for calendar";
20913         }
20914         
20915         var mark = {
20916             tag: "div",
20917             cls:"x-dlg-mask",
20918             style: "text-align:center",
20919             cn: [
20920                 {
20921                     tag: "div",
20922                     style: "background-color:white;width:50%;margin:250 auto",
20923                     cn: [
20924                         {
20925                             tag: "img",
20926                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20927                         },
20928                         {
20929                             tag: "span",
20930                             html: "Loading"
20931                         }
20932                         
20933                     ]
20934                 }
20935             ]
20936         };
20937         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20938         
20939         var size = this.el.select('.fc-content', true).first().getSize();
20940         this.maskEl.setSize(size.width, size.height);
20941         this.maskEl.enableDisplayMode("block");
20942         if(!this.loadMask){
20943             this.maskEl.hide();
20944         }
20945         
20946         this.store = Roo.factory(this.store, Roo.data);
20947         this.store.on('load', this.onLoad, this);
20948         this.store.on('beforeload', this.onBeforeLoad, this);
20949         
20950         this.resize();
20951         
20952         this.cells = this.el.select('.fc-day',true);
20953         //Roo.log(this.cells);
20954         this.textNodes = this.el.query('.fc-day-number');
20955         this.cells.addClassOnOver('fc-state-hover');
20956         
20957         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20958         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20959         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20960         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20961         
20962         this.on('monthchange', this.onMonthChange, this);
20963         
20964         this.update(new Date().clearTime());
20965     },
20966     
20967     resize : function() {
20968         var sz  = this.el.getSize();
20969         
20970         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20971         this.el.select('.fc-day-content div',true).setHeight(34);
20972     },
20973     
20974     
20975     // private
20976     showPrevMonth : function(e){
20977         this.update(this.activeDate.add("mo", -1));
20978     },
20979     showToday : function(e){
20980         this.update(new Date().clearTime());
20981     },
20982     // private
20983     showNextMonth : function(e){
20984         this.update(this.activeDate.add("mo", 1));
20985     },
20986
20987     // private
20988     showPrevYear : function(){
20989         this.update(this.activeDate.add("y", -1));
20990     },
20991
20992     // private
20993     showNextYear : function(){
20994         this.update(this.activeDate.add("y", 1));
20995     },
20996
20997     
20998    // private
20999     update : function(date)
21000     {
21001         var vd = this.activeDate;
21002         this.activeDate = date;
21003 //        if(vd && this.el){
21004 //            var t = date.getTime();
21005 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21006 //                Roo.log('using add remove');
21007 //                
21008 //                this.fireEvent('monthchange', this, date);
21009 //                
21010 //                this.cells.removeClass("fc-state-highlight");
21011 //                this.cells.each(function(c){
21012 //                   if(c.dateValue == t){
21013 //                       c.addClass("fc-state-highlight");
21014 //                       setTimeout(function(){
21015 //                            try{c.dom.firstChild.focus();}catch(e){}
21016 //                       }, 50);
21017 //                       return false;
21018 //                   }
21019 //                   return true;
21020 //                });
21021 //                return;
21022 //            }
21023 //        }
21024         
21025         var days = date.getDaysInMonth();
21026         
21027         var firstOfMonth = date.getFirstDateOfMonth();
21028         var startingPos = firstOfMonth.getDay()-this.startDay;
21029         
21030         if(startingPos < this.startDay){
21031             startingPos += 7;
21032         }
21033         
21034         var pm = date.add(Date.MONTH, -1);
21035         var prevStart = pm.getDaysInMonth()-startingPos;
21036 //        
21037         this.cells = this.el.select('.fc-day',true);
21038         this.textNodes = this.el.query('.fc-day-number');
21039         this.cells.addClassOnOver('fc-state-hover');
21040         
21041         var cells = this.cells.elements;
21042         var textEls = this.textNodes;
21043         
21044         Roo.each(cells, function(cell){
21045             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21046         });
21047         
21048         days += startingPos;
21049
21050         // convert everything to numbers so it's fast
21051         var day = 86400000;
21052         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21053         //Roo.log(d);
21054         //Roo.log(pm);
21055         //Roo.log(prevStart);
21056         
21057         var today = new Date().clearTime().getTime();
21058         var sel = date.clearTime().getTime();
21059         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21060         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21061         var ddMatch = this.disabledDatesRE;
21062         var ddText = this.disabledDatesText;
21063         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21064         var ddaysText = this.disabledDaysText;
21065         var format = this.format;
21066         
21067         var setCellClass = function(cal, cell){
21068             cell.row = 0;
21069             cell.events = [];
21070             cell.more = [];
21071             //Roo.log('set Cell Class');
21072             cell.title = "";
21073             var t = d.getTime();
21074             
21075             //Roo.log(d);
21076             
21077             cell.dateValue = t;
21078             if(t == today){
21079                 cell.className += " fc-today";
21080                 cell.className += " fc-state-highlight";
21081                 cell.title = cal.todayText;
21082             }
21083             if(t == sel){
21084                 // disable highlight in other month..
21085                 //cell.className += " fc-state-highlight";
21086                 
21087             }
21088             // disabling
21089             if(t < min) {
21090                 cell.className = " fc-state-disabled";
21091                 cell.title = cal.minText;
21092                 return;
21093             }
21094             if(t > max) {
21095                 cell.className = " fc-state-disabled";
21096                 cell.title = cal.maxText;
21097                 return;
21098             }
21099             if(ddays){
21100                 if(ddays.indexOf(d.getDay()) != -1){
21101                     cell.title = ddaysText;
21102                     cell.className = " fc-state-disabled";
21103                 }
21104             }
21105             if(ddMatch && format){
21106                 var fvalue = d.dateFormat(format);
21107                 if(ddMatch.test(fvalue)){
21108                     cell.title = ddText.replace("%0", fvalue);
21109                     cell.className = " fc-state-disabled";
21110                 }
21111             }
21112             
21113             if (!cell.initialClassName) {
21114                 cell.initialClassName = cell.dom.className;
21115             }
21116             
21117             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21118         };
21119
21120         var i = 0;
21121         
21122         for(; i < startingPos; i++) {
21123             textEls[i].innerHTML = (++prevStart);
21124             d.setDate(d.getDate()+1);
21125             
21126             cells[i].className = "fc-past fc-other-month";
21127             setCellClass(this, cells[i]);
21128         }
21129         
21130         var intDay = 0;
21131         
21132         for(; i < days; i++){
21133             intDay = i - startingPos + 1;
21134             textEls[i].innerHTML = (intDay);
21135             d.setDate(d.getDate()+1);
21136             
21137             cells[i].className = ''; // "x-date-active";
21138             setCellClass(this, cells[i]);
21139         }
21140         var extraDays = 0;
21141         
21142         for(; i < 42; i++) {
21143             textEls[i].innerHTML = (++extraDays);
21144             d.setDate(d.getDate()+1);
21145             
21146             cells[i].className = "fc-future fc-other-month";
21147             setCellClass(this, cells[i]);
21148         }
21149         
21150         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21151         
21152         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21153         
21154         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21155         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21156         
21157         if(totalRows != 6){
21158             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21159             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21160         }
21161         
21162         this.fireEvent('monthchange', this, date);
21163         
21164         
21165         /*
21166         if(!this.internalRender){
21167             var main = this.el.dom.firstChild;
21168             var w = main.offsetWidth;
21169             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21170             Roo.fly(main).setWidth(w);
21171             this.internalRender = true;
21172             // opera does not respect the auto grow header center column
21173             // then, after it gets a width opera refuses to recalculate
21174             // without a second pass
21175             if(Roo.isOpera && !this.secondPass){
21176                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21177                 this.secondPass = true;
21178                 this.update.defer(10, this, [date]);
21179             }
21180         }
21181         */
21182         
21183     },
21184     
21185     findCell : function(dt) {
21186         dt = dt.clearTime().getTime();
21187         var ret = false;
21188         this.cells.each(function(c){
21189             //Roo.log("check " +c.dateValue + '?=' + dt);
21190             if(c.dateValue == dt){
21191                 ret = c;
21192                 return false;
21193             }
21194             return true;
21195         });
21196         
21197         return ret;
21198     },
21199     
21200     findCells : function(ev) {
21201         var s = ev.start.clone().clearTime().getTime();
21202        // Roo.log(s);
21203         var e= ev.end.clone().clearTime().getTime();
21204        // Roo.log(e);
21205         var ret = [];
21206         this.cells.each(function(c){
21207              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21208             
21209             if(c.dateValue > e){
21210                 return ;
21211             }
21212             if(c.dateValue < s){
21213                 return ;
21214             }
21215             ret.push(c);
21216         });
21217         
21218         return ret;    
21219     },
21220     
21221 //    findBestRow: function(cells)
21222 //    {
21223 //        var ret = 0;
21224 //        
21225 //        for (var i =0 ; i < cells.length;i++) {
21226 //            ret  = Math.max(cells[i].rows || 0,ret);
21227 //        }
21228 //        return ret;
21229 //        
21230 //    },
21231     
21232     
21233     addItem : function(ev)
21234     {
21235         // look for vertical location slot in
21236         var cells = this.findCells(ev);
21237         
21238 //        ev.row = this.findBestRow(cells);
21239         
21240         // work out the location.
21241         
21242         var crow = false;
21243         var rows = [];
21244         for(var i =0; i < cells.length; i++) {
21245             
21246             cells[i].row = cells[0].row;
21247             
21248             if(i == 0){
21249                 cells[i].row = cells[i].row + 1;
21250             }
21251             
21252             if (!crow) {
21253                 crow = {
21254                     start : cells[i],
21255                     end :  cells[i]
21256                 };
21257                 continue;
21258             }
21259             if (crow.start.getY() == cells[i].getY()) {
21260                 // on same row.
21261                 crow.end = cells[i];
21262                 continue;
21263             }
21264             // different row.
21265             rows.push(crow);
21266             crow = {
21267                 start: cells[i],
21268                 end : cells[i]
21269             };
21270             
21271         }
21272         
21273         rows.push(crow);
21274         ev.els = [];
21275         ev.rows = rows;
21276         ev.cells = cells;
21277         
21278         cells[0].events.push(ev);
21279         
21280         this.calevents.push(ev);
21281     },
21282     
21283     clearEvents: function() {
21284         
21285         if(!this.calevents){
21286             return;
21287         }
21288         
21289         Roo.each(this.cells.elements, function(c){
21290             c.row = 0;
21291             c.events = [];
21292             c.more = [];
21293         });
21294         
21295         Roo.each(this.calevents, function(e) {
21296             Roo.each(e.els, function(el) {
21297                 el.un('mouseenter' ,this.onEventEnter, this);
21298                 el.un('mouseleave' ,this.onEventLeave, this);
21299                 el.remove();
21300             },this);
21301         },this);
21302         
21303         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21304             e.remove();
21305         });
21306         
21307     },
21308     
21309     renderEvents: function()
21310     {   
21311         var _this = this;
21312         
21313         this.cells.each(function(c) {
21314             
21315             if(c.row < 5){
21316                 return;
21317             }
21318             
21319             var ev = c.events;
21320             
21321             var r = 4;
21322             if(c.row != c.events.length){
21323                 r = 4 - (4 - (c.row - c.events.length));
21324             }
21325             
21326             c.events = ev.slice(0, r);
21327             c.more = ev.slice(r);
21328             
21329             if(c.more.length && c.more.length == 1){
21330                 c.events.push(c.more.pop());
21331             }
21332             
21333             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21334             
21335         });
21336             
21337         this.cells.each(function(c) {
21338             
21339             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21340             
21341             
21342             for (var e = 0; e < c.events.length; e++){
21343                 var ev = c.events[e];
21344                 var rows = ev.rows;
21345                 
21346                 for(var i = 0; i < rows.length; i++) {
21347                 
21348                     // how many rows should it span..
21349
21350                     var  cfg = {
21351                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21352                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21353
21354                         unselectable : "on",
21355                         cn : [
21356                             {
21357                                 cls: 'fc-event-inner',
21358                                 cn : [
21359     //                                {
21360     //                                  tag:'span',
21361     //                                  cls: 'fc-event-time',
21362     //                                  html : cells.length > 1 ? '' : ev.time
21363     //                                },
21364                                     {
21365                                       tag:'span',
21366                                       cls: 'fc-event-title',
21367                                       html : String.format('{0}', ev.title)
21368                                     }
21369
21370
21371                                 ]
21372                             },
21373                             {
21374                                 cls: 'ui-resizable-handle ui-resizable-e',
21375                                 html : '&nbsp;&nbsp;&nbsp'
21376                             }
21377
21378                         ]
21379                     };
21380
21381                     if (i == 0) {
21382                         cfg.cls += ' fc-event-start';
21383                     }
21384                     if ((i+1) == rows.length) {
21385                         cfg.cls += ' fc-event-end';
21386                     }
21387
21388                     var ctr = _this.el.select('.fc-event-container',true).first();
21389                     var cg = ctr.createChild(cfg);
21390
21391                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21392                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21393
21394                     var r = (c.more.length) ? 1 : 0;
21395                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21396                     cg.setWidth(ebox.right - sbox.x -2);
21397
21398                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21399                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21400                     cg.on('click', _this.onEventClick, _this, ev);
21401
21402                     ev.els.push(cg);
21403                     
21404                 }
21405                 
21406             }
21407             
21408             
21409             if(c.more.length){
21410                 var  cfg = {
21411                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21412                     style : 'position: absolute',
21413                     unselectable : "on",
21414                     cn : [
21415                         {
21416                             cls: 'fc-event-inner',
21417                             cn : [
21418                                 {
21419                                   tag:'span',
21420                                   cls: 'fc-event-title',
21421                                   html : 'More'
21422                                 }
21423
21424
21425                             ]
21426                         },
21427                         {
21428                             cls: 'ui-resizable-handle ui-resizable-e',
21429                             html : '&nbsp;&nbsp;&nbsp'
21430                         }
21431
21432                     ]
21433                 };
21434
21435                 var ctr = _this.el.select('.fc-event-container',true).first();
21436                 var cg = ctr.createChild(cfg);
21437
21438                 var sbox = c.select('.fc-day-content',true).first().getBox();
21439                 var ebox = c.select('.fc-day-content',true).first().getBox();
21440                 //Roo.log(cg);
21441                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21442                 cg.setWidth(ebox.right - sbox.x -2);
21443
21444                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21445                 
21446             }
21447             
21448         });
21449         
21450         
21451         
21452     },
21453     
21454     onEventEnter: function (e, el,event,d) {
21455         this.fireEvent('evententer', this, el, event);
21456     },
21457     
21458     onEventLeave: function (e, el,event,d) {
21459         this.fireEvent('eventleave', this, el, event);
21460     },
21461     
21462     onEventClick: function (e, el,event,d) {
21463         this.fireEvent('eventclick', this, el, event);
21464     },
21465     
21466     onMonthChange: function () {
21467         this.store.load();
21468     },
21469     
21470     onMoreEventClick: function(e, el, more)
21471     {
21472         var _this = this;
21473         
21474         this.calpopover.placement = 'right';
21475         this.calpopover.setTitle('More');
21476         
21477         this.calpopover.setContent('');
21478         
21479         var ctr = this.calpopover.el.select('.popover-content', true).first();
21480         
21481         Roo.each(more, function(m){
21482             var cfg = {
21483                 cls : 'fc-event-hori fc-event-draggable',
21484                 html : m.title
21485             };
21486             var cg = ctr.createChild(cfg);
21487             
21488             cg.on('click', _this.onEventClick, _this, m);
21489         });
21490         
21491         this.calpopover.show(el);
21492         
21493         
21494     },
21495     
21496     onLoad: function () 
21497     {   
21498         this.calevents = [];
21499         var cal = this;
21500         
21501         if(this.store.getCount() > 0){
21502             this.store.data.each(function(d){
21503                cal.addItem({
21504                     id : d.data.id,
21505                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21506                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21507                     time : d.data.start_time,
21508                     title : d.data.title,
21509                     description : d.data.description,
21510                     venue : d.data.venue
21511                 });
21512             });
21513         }
21514         
21515         this.renderEvents();
21516         
21517         if(this.calevents.length && this.loadMask){
21518             this.maskEl.hide();
21519         }
21520     },
21521     
21522     onBeforeLoad: function()
21523     {
21524         this.clearEvents();
21525         if(this.loadMask){
21526             this.maskEl.show();
21527         }
21528     }
21529 });
21530
21531  
21532  /*
21533  * - LGPL
21534  *
21535  * element
21536  * 
21537  */
21538
21539 /**
21540  * @class Roo.bootstrap.Popover
21541  * @extends Roo.bootstrap.Component
21542  * @parent none builder
21543  * @children Roo.bootstrap.Component
21544  * Bootstrap Popover class
21545  * @cfg {String} html contents of the popover   (or false to use children..)
21546  * @cfg {String} title of popover (or false to hide)
21547  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21548  * @cfg {String} trigger click || hover (or false to trigger manually)
21549  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21550  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21551  *      - if false and it has a 'parent' then it will be automatically added to that element
21552  *      - if string - Roo.get  will be called 
21553  * @cfg {Number} delay - delay before showing
21554  
21555  * @constructor
21556  * Create a new Popover
21557  * @param {Object} config The config object
21558  */
21559
21560 Roo.bootstrap.Popover = function(config){
21561     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21562     
21563     this.addEvents({
21564         // raw events
21565          /**
21566          * @event show
21567          * After the popover show
21568          * 
21569          * @param {Roo.bootstrap.Popover} this
21570          */
21571         "show" : true,
21572         /**
21573          * @event hide
21574          * After the popover hide
21575          * 
21576          * @param {Roo.bootstrap.Popover} this
21577          */
21578         "hide" : true
21579     });
21580 };
21581
21582 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21583     
21584     title: false,
21585     html: false,
21586     
21587     placement : 'right',
21588     trigger : 'hover', // hover
21589     modal : false,
21590     delay : 0,
21591     
21592     over: false,
21593     
21594     can_build_overlaid : false,
21595     
21596     maskEl : false, // the mask element
21597     headerEl : false,
21598     contentEl : false,
21599     alignEl : false, // when show is called with an element - this get's stored.
21600     
21601     getChildContainer : function()
21602     {
21603         return this.contentEl;
21604         
21605     },
21606     getPopoverHeader : function()
21607     {
21608         this.title = true; // flag not to hide it..
21609         this.headerEl.addClass('p-0');
21610         return this.headerEl
21611     },
21612     
21613     
21614     getAutoCreate : function(){
21615          
21616         var cfg = {
21617            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21618            style: 'display:block',
21619            cn : [
21620                 {
21621                     cls : 'arrow'
21622                 },
21623                 {
21624                     cls : 'popover-inner ',
21625                     cn : [
21626                         {
21627                             tag: 'h3',
21628                             cls: 'popover-title popover-header',
21629                             html : this.title === false ? '' : this.title
21630                         },
21631                         {
21632                             cls : 'popover-content popover-body '  + (this.cls || ''),
21633                             html : this.html || ''
21634                         }
21635                     ]
21636                     
21637                 }
21638            ]
21639         };
21640         
21641         return cfg;
21642     },
21643     /**
21644      * @param {string} the title
21645      */
21646     setTitle: function(str)
21647     {
21648         this.title = str;
21649         if (this.el) {
21650             this.headerEl.dom.innerHTML = str;
21651         }
21652         
21653     },
21654     /**
21655      * @param {string} the body content
21656      */
21657     setContent: function(str)
21658     {
21659         this.html = str;
21660         if (this.contentEl) {
21661             this.contentEl.dom.innerHTML = str;
21662         }
21663         
21664     },
21665     // as it get's added to the bottom of the page.
21666     onRender : function(ct, position)
21667     {
21668         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21669         
21670         
21671         
21672         if(!this.el){
21673             var cfg = Roo.apply({},  this.getAutoCreate());
21674             cfg.id = Roo.id();
21675             
21676             if (this.cls) {
21677                 cfg.cls += ' ' + this.cls;
21678             }
21679             if (this.style) {
21680                 cfg.style = this.style;
21681             }
21682             //Roo.log("adding to ");
21683             this.el = Roo.get(document.body).createChild(cfg, position);
21684 //            Roo.log(this.el);
21685         }
21686         
21687         this.contentEl = this.el.select('.popover-content',true).first();
21688         this.headerEl =  this.el.select('.popover-title',true).first();
21689         
21690         var nitems = [];
21691         if(typeof(this.items) != 'undefined'){
21692             var items = this.items;
21693             delete this.items;
21694
21695             for(var i =0;i < items.length;i++) {
21696                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21697             }
21698         }
21699
21700         this.items = nitems;
21701         
21702         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21703         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21704         
21705         
21706         
21707         this.initEvents();
21708     },
21709     
21710     resizeMask : function()
21711     {
21712         this.maskEl.setSize(
21713             Roo.lib.Dom.getViewWidth(true),
21714             Roo.lib.Dom.getViewHeight(true)
21715         );
21716     },
21717     
21718     initEvents : function()
21719     {
21720         
21721         if (!this.modal) { 
21722             Roo.bootstrap.Popover.register(this);
21723         }
21724          
21725         this.arrowEl = this.el.select('.arrow',true).first();
21726         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21727         this.el.enableDisplayMode('block');
21728         this.el.hide();
21729  
21730         
21731         if (this.over === false && !this.parent()) {
21732             return; 
21733         }
21734         if (this.triggers === false) {
21735             return;
21736         }
21737          
21738         // support parent
21739         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21740         var triggers = this.trigger ? this.trigger.split(' ') : [];
21741         Roo.each(triggers, function(trigger) {
21742         
21743             if (trigger == 'click') {
21744                 on_el.on('click', this.toggle, this);
21745             } else if (trigger != 'manual') {
21746                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21747                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21748       
21749                 on_el.on(eventIn  ,this.enter, this);
21750                 on_el.on(eventOut, this.leave, this);
21751             }
21752         }, this);
21753     },
21754     
21755     
21756     // private
21757     timeout : null,
21758     hoverState : null,
21759     
21760     toggle : function () {
21761         this.hoverState == 'in' ? this.leave() : this.enter();
21762     },
21763     
21764     enter : function () {
21765         
21766         clearTimeout(this.timeout);
21767     
21768         this.hoverState = 'in';
21769     
21770         if (!this.delay || !this.delay.show) {
21771             this.show();
21772             return;
21773         }
21774         var _t = this;
21775         this.timeout = setTimeout(function () {
21776             if (_t.hoverState == 'in') {
21777                 _t.show();
21778             }
21779         }, this.delay.show)
21780     },
21781     
21782     leave : function() {
21783         clearTimeout(this.timeout);
21784     
21785         this.hoverState = 'out';
21786     
21787         if (!this.delay || !this.delay.hide) {
21788             this.hide();
21789             return;
21790         }
21791         var _t = this;
21792         this.timeout = setTimeout(function () {
21793             if (_t.hoverState == 'out') {
21794                 _t.hide();
21795             }
21796         }, this.delay.hide)
21797     },
21798     
21799     /**
21800      * update the position of the dialog
21801      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21802      * 
21803      *
21804      */
21805     
21806     doAlign : function()
21807     {
21808         
21809         if (this.alignEl) {
21810             this.updatePosition(this.placement, true);
21811              
21812         } else {
21813             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21814             var es = this.el.getSize();
21815             var x = Roo.lib.Dom.getViewWidth()/2;
21816             var y = Roo.lib.Dom.getViewHeight()/2;
21817             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21818             
21819         }
21820
21821          
21822          
21823         
21824         
21825     },
21826     
21827     /**
21828      * Show the popover
21829      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21830      * @param {string} (left|right|top|bottom) position
21831      */
21832     show : function (on_el, placement)
21833     {
21834         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21835         on_el = on_el || false; // default to false
21836          
21837         if (!on_el) {
21838             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21839                 on_el = this.parent().el;
21840             } else if (this.over) {
21841                 on_el = Roo.get(this.over);
21842             }
21843             
21844         }
21845         
21846         this.alignEl = Roo.get( on_el );
21847
21848         if (!this.el) {
21849             this.render(document.body);
21850         }
21851         
21852         
21853          
21854         
21855         if (this.title === false) {
21856             this.headerEl.hide();
21857         }
21858         
21859        
21860         this.el.show();
21861         this.el.dom.style.display = 'block';
21862          
21863         this.doAlign();
21864         
21865         //var arrow = this.el.select('.arrow',true).first();
21866         //arrow.set(align[2], 
21867         
21868         this.el.addClass('in');
21869         
21870          
21871         
21872         this.hoverState = 'in';
21873         
21874         if (this.modal) {
21875             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21876             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21877             this.maskEl.dom.style.display = 'block';
21878             this.maskEl.addClass('show');
21879         }
21880         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21881  
21882         this.fireEvent('show', this);
21883         
21884     },
21885     /**
21886      * fire this manually after loading a grid in the table for example
21887      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21888      * @param {Boolean} try and move it if we cant get right position.
21889      */
21890     updatePosition : function(placement, try_move)
21891     {
21892         // allow for calling with no parameters
21893         placement = placement   ? placement :  this.placement;
21894         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21895         
21896         this.el.removeClass([
21897             'fade','top','bottom', 'left', 'right','in',
21898             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21899         ]);
21900         this.el.addClass(placement + ' bs-popover-' + placement);
21901         
21902         if (!this.alignEl ) {
21903             return false;
21904         }
21905         
21906         switch (placement) {
21907             case 'right':
21908                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21909                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21910                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21911                     //normal display... or moved up/down.
21912                     this.el.setXY(offset);
21913                     var xy = this.alignEl.getAnchorXY('tr', false);
21914                     xy[0]+=2;xy[1]+=5;
21915                     this.arrowEl.setXY(xy);
21916                     return true;
21917                 }
21918                 // continue through...
21919                 return this.updatePosition('left', false);
21920                 
21921             
21922             case 'left':
21923                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21924                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21925                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21926                     //normal display... or moved up/down.
21927                     this.el.setXY(offset);
21928                     var xy = this.alignEl.getAnchorXY('tl', false);
21929                     xy[0]-=10;xy[1]+=5; // << fix me
21930                     this.arrowEl.setXY(xy);
21931                     return true;
21932                 }
21933                 // call self...
21934                 return this.updatePosition('right', false);
21935             
21936             case 'top':
21937                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21938                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21939                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21940                     //normal display... or moved up/down.
21941                     this.el.setXY(offset);
21942                     var xy = this.alignEl.getAnchorXY('t', false);
21943                     xy[1]-=10; // << fix me
21944                     this.arrowEl.setXY(xy);
21945                     return true;
21946                 }
21947                 // fall through
21948                return this.updatePosition('bottom', false);
21949             
21950             case 'bottom':
21951                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21952                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21953                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21954                     //normal display... or moved up/down.
21955                     this.el.setXY(offset);
21956                     var xy = this.alignEl.getAnchorXY('b', false);
21957                      xy[1]+=2; // << fix me
21958                     this.arrowEl.setXY(xy);
21959                     return true;
21960                 }
21961                 // fall through
21962                 return this.updatePosition('top', false);
21963                 
21964             
21965         }
21966         
21967         
21968         return false;
21969     },
21970     
21971     hide : function()
21972     {
21973         this.el.setXY([0,0]);
21974         this.el.removeClass('in');
21975         this.el.hide();
21976         this.hoverState = null;
21977         this.maskEl.hide(); // always..
21978         this.fireEvent('hide', this);
21979     }
21980     
21981 });
21982
21983
21984 Roo.apply(Roo.bootstrap.Popover, {
21985
21986     alignment : {
21987         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21988         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21989         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21990         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21991     },
21992     
21993     zIndex : 20001,
21994
21995     clickHander : false,
21996     
21997     
21998
21999     onMouseDown : function(e)
22000     {
22001         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22002             /// what is nothing is showing..
22003             this.hideAll();
22004         }
22005          
22006     },
22007     
22008     
22009     popups : [],
22010     
22011     register : function(popup)
22012     {
22013         if (!Roo.bootstrap.Popover.clickHandler) {
22014             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22015         }
22016         // hide other popups.
22017         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22018         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22019         this.hideAll(); //<< why?
22020         //this.popups.push(popup);
22021     },
22022     hideAll : function()
22023     {
22024         this.popups.forEach(function(p) {
22025             p.hide();
22026         });
22027     },
22028     onShow : function() {
22029         Roo.bootstrap.Popover.popups.push(this);
22030     },
22031     onHide : function() {
22032         Roo.bootstrap.Popover.popups.remove(this);
22033     } 
22034
22035 });
22036 /**
22037  * @class Roo.bootstrap.PopoverNav
22038  * @extends Roo.bootstrap.nav.Simplebar
22039  * @parent Roo.bootstrap.Popover
22040  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22041  * @licence LGPL
22042  * Bootstrap Popover header navigation class
22043  * FIXME? should this go under nav?
22044  *
22045  * 
22046  * @constructor
22047  * Create a new Popover Header Navigation 
22048  * @param {Object} config The config object
22049  */
22050
22051 Roo.bootstrap.PopoverNav = function(config){
22052     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22053 };
22054
22055 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22056     
22057     
22058     container_method : 'getPopoverHeader' 
22059     
22060      
22061     
22062     
22063    
22064 });
22065
22066  
22067
22068  /*
22069  * - LGPL
22070  *
22071  * Progress
22072  * 
22073  */
22074
22075 /**
22076  * @class Roo.bootstrap.Progress
22077  * @extends Roo.bootstrap.Component
22078  * @children Roo.bootstrap.ProgressBar
22079  * Bootstrap Progress class
22080  * @cfg {Boolean} striped striped of the progress bar
22081  * @cfg {Boolean} active animated of the progress bar
22082  * 
22083  * 
22084  * @constructor
22085  * Create a new Progress
22086  * @param {Object} config The config object
22087  */
22088
22089 Roo.bootstrap.Progress = function(config){
22090     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22091 };
22092
22093 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22094     
22095     striped : false,
22096     active: false,
22097     
22098     getAutoCreate : function(){
22099         var cfg = {
22100             tag: 'div',
22101             cls: 'progress'
22102         };
22103         
22104         
22105         if(this.striped){
22106             cfg.cls += ' progress-striped';
22107         }
22108       
22109         if(this.active){
22110             cfg.cls += ' active';
22111         }
22112         
22113         
22114         return cfg;
22115     }
22116    
22117 });
22118
22119  
22120
22121  /*
22122  * - LGPL
22123  *
22124  * ProgressBar
22125  * 
22126  */
22127
22128 /**
22129  * @class Roo.bootstrap.ProgressBar
22130  * @extends Roo.bootstrap.Component
22131  * Bootstrap ProgressBar class
22132  * @cfg {Number} aria_valuenow aria-value now
22133  * @cfg {Number} aria_valuemin aria-value min
22134  * @cfg {Number} aria_valuemax aria-value max
22135  * @cfg {String} label label for the progress bar
22136  * @cfg {String} panel (success | info | warning | danger )
22137  * @cfg {String} role role of the progress bar
22138  * @cfg {String} sr_only text
22139  * 
22140  * 
22141  * @constructor
22142  * Create a new ProgressBar
22143  * @param {Object} config The config object
22144  */
22145
22146 Roo.bootstrap.ProgressBar = function(config){
22147     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22148 };
22149
22150 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22151     
22152     aria_valuenow : 0,
22153     aria_valuemin : 0,
22154     aria_valuemax : 100,
22155     label : false,
22156     panel : false,
22157     role : false,
22158     sr_only: false,
22159     
22160     getAutoCreate : function()
22161     {
22162         
22163         var cfg = {
22164             tag: 'div',
22165             cls: 'progress-bar',
22166             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22167         };
22168         
22169         if(this.sr_only){
22170             cfg.cn = {
22171                 tag: 'span',
22172                 cls: 'sr-only',
22173                 html: this.sr_only
22174             }
22175         }
22176         
22177         if(this.role){
22178             cfg.role = this.role;
22179         }
22180         
22181         if(this.aria_valuenow){
22182             cfg['aria-valuenow'] = this.aria_valuenow;
22183         }
22184         
22185         if(this.aria_valuemin){
22186             cfg['aria-valuemin'] = this.aria_valuemin;
22187         }
22188         
22189         if(this.aria_valuemax){
22190             cfg['aria-valuemax'] = this.aria_valuemax;
22191         }
22192         
22193         if(this.label && !this.sr_only){
22194             cfg.html = this.label;
22195         }
22196         
22197         if(this.panel){
22198             cfg.cls += ' progress-bar-' + this.panel;
22199         }
22200         
22201         return cfg;
22202     },
22203     
22204     update : function(aria_valuenow)
22205     {
22206         this.aria_valuenow = aria_valuenow;
22207         
22208         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22209     }
22210    
22211 });
22212
22213  
22214
22215  /**
22216  * @class Roo.bootstrap.TabGroup
22217  * @extends Roo.bootstrap.Column
22218  * @children Roo.bootstrap.TabPanel
22219  * Bootstrap Column class
22220  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22221  * @cfg {Boolean} carousel true to make the group behave like a carousel
22222  * @cfg {Boolean} bullets show bullets for the panels
22223  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22224  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22225  * @cfg {Boolean} showarrow (true|false) show arrow default true
22226  * 
22227  * @constructor
22228  * Create a new TabGroup
22229  * @param {Object} config The config object
22230  */
22231
22232 Roo.bootstrap.TabGroup = function(config){
22233     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22234     if (!this.navId) {
22235         this.navId = Roo.id();
22236     }
22237     this.tabs = [];
22238     Roo.bootstrap.TabGroup.register(this);
22239     
22240 };
22241
22242 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22243     
22244     carousel : false,
22245     transition : false,
22246     bullets : 0,
22247     timer : 0,
22248     autoslide : false,
22249     slideFn : false,
22250     slideOnTouch : false,
22251     showarrow : true,
22252     
22253     getAutoCreate : function()
22254     {
22255         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22256         
22257         cfg.cls += ' tab-content';
22258         
22259         if (this.carousel) {
22260             cfg.cls += ' carousel slide';
22261             
22262             cfg.cn = [{
22263                cls : 'carousel-inner',
22264                cn : []
22265             }];
22266         
22267             if(this.bullets  && !Roo.isTouch){
22268                 
22269                 var bullets = {
22270                     cls : 'carousel-bullets',
22271                     cn : []
22272                 };
22273                
22274                 if(this.bullets_cls){
22275                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22276                 }
22277                 
22278                 bullets.cn.push({
22279                     cls : 'clear'
22280                 });
22281                 
22282                 cfg.cn[0].cn.push(bullets);
22283             }
22284             
22285             if(this.showarrow){
22286                 cfg.cn[0].cn.push({
22287                     tag : 'div',
22288                     class : 'carousel-arrow',
22289                     cn : [
22290                         {
22291                             tag : 'div',
22292                             class : 'carousel-prev',
22293                             cn : [
22294                                 {
22295                                     tag : 'i',
22296                                     class : 'fa fa-chevron-left'
22297                                 }
22298                             ]
22299                         },
22300                         {
22301                             tag : 'div',
22302                             class : 'carousel-next',
22303                             cn : [
22304                                 {
22305                                     tag : 'i',
22306                                     class : 'fa fa-chevron-right'
22307                                 }
22308                             ]
22309                         }
22310                     ]
22311                 });
22312             }
22313             
22314         }
22315         
22316         return cfg;
22317     },
22318     
22319     initEvents:  function()
22320     {
22321 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22322 //            this.el.on("touchstart", this.onTouchStart, this);
22323 //        }
22324         
22325         if(this.autoslide){
22326             var _this = this;
22327             
22328             this.slideFn = window.setInterval(function() {
22329                 _this.showPanelNext();
22330             }, this.timer);
22331         }
22332         
22333         if(this.showarrow){
22334             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22335             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22336         }
22337         
22338         
22339     },
22340     
22341 //    onTouchStart : function(e, el, o)
22342 //    {
22343 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22344 //            return;
22345 //        }
22346 //        
22347 //        this.showPanelNext();
22348 //    },
22349     
22350     
22351     getChildContainer : function()
22352     {
22353         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22354     },
22355     
22356     /**
22357     * register a Navigation item
22358     * @param {Roo.bootstrap.nav.Item} the navitem to add
22359     */
22360     register : function(item)
22361     {
22362         this.tabs.push( item);
22363         item.navId = this.navId; // not really needed..
22364         this.addBullet();
22365     
22366     },
22367     
22368     getActivePanel : function()
22369     {
22370         var r = false;
22371         Roo.each(this.tabs, function(t) {
22372             if (t.active) {
22373                 r = t;
22374                 return false;
22375             }
22376             return null;
22377         });
22378         return r;
22379         
22380     },
22381     getPanelByName : function(n)
22382     {
22383         var r = false;
22384         Roo.each(this.tabs, function(t) {
22385             if (t.tabId == n) {
22386                 r = t;
22387                 return false;
22388             }
22389             return null;
22390         });
22391         return r;
22392     },
22393     indexOfPanel : function(p)
22394     {
22395         var r = false;
22396         Roo.each(this.tabs, function(t,i) {
22397             if (t.tabId == p.tabId) {
22398                 r = i;
22399                 return false;
22400             }
22401             return null;
22402         });
22403         return r;
22404     },
22405     /**
22406      * show a specific panel
22407      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22408      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22409      */
22410     showPanel : function (pan)
22411     {
22412         if(this.transition || typeof(pan) == 'undefined'){
22413             Roo.log("waiting for the transitionend");
22414             return false;
22415         }
22416         
22417         if (typeof(pan) == 'number') {
22418             pan = this.tabs[pan];
22419         }
22420         
22421         if (typeof(pan) == 'string') {
22422             pan = this.getPanelByName(pan);
22423         }
22424         
22425         var cur = this.getActivePanel();
22426         
22427         if(!pan || !cur){
22428             Roo.log('pan or acitve pan is undefined');
22429             return false;
22430         }
22431         
22432         if (pan.tabId == this.getActivePanel().tabId) {
22433             return true;
22434         }
22435         
22436         if (false === cur.fireEvent('beforedeactivate')) {
22437             return false;
22438         }
22439         
22440         if(this.bullets > 0 && !Roo.isTouch){
22441             this.setActiveBullet(this.indexOfPanel(pan));
22442         }
22443         
22444         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22445             
22446             //class="carousel-item carousel-item-next carousel-item-left"
22447             
22448             this.transition = true;
22449             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22450             var lr = dir == 'next' ? 'left' : 'right';
22451             pan.el.addClass(dir); // or prev
22452             pan.el.addClass('carousel-item-' + dir); // or prev
22453             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22454             cur.el.addClass(lr); // or right
22455             pan.el.addClass(lr);
22456             cur.el.addClass('carousel-item-' +lr); // or right
22457             pan.el.addClass('carousel-item-' +lr);
22458             
22459             
22460             var _this = this;
22461             cur.el.on('transitionend', function() {
22462                 Roo.log("trans end?");
22463                 
22464                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22465                 pan.setActive(true);
22466                 
22467                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22468                 cur.setActive(false);
22469                 
22470                 _this.transition = false;
22471                 
22472             }, this, { single:  true } );
22473             
22474             return true;
22475         }
22476         
22477         cur.setActive(false);
22478         pan.setActive(true);
22479         
22480         return true;
22481         
22482     },
22483     showPanelNext : function()
22484     {
22485         var i = this.indexOfPanel(this.getActivePanel());
22486         
22487         if (i >= this.tabs.length - 1 && !this.autoslide) {
22488             return;
22489         }
22490         
22491         if (i >= this.tabs.length - 1 && this.autoslide) {
22492             i = -1;
22493         }
22494         
22495         this.showPanel(this.tabs[i+1]);
22496     },
22497     
22498     showPanelPrev : function()
22499     {
22500         var i = this.indexOfPanel(this.getActivePanel());
22501         
22502         if (i  < 1 && !this.autoslide) {
22503             return;
22504         }
22505         
22506         if (i < 1 && this.autoslide) {
22507             i = this.tabs.length;
22508         }
22509         
22510         this.showPanel(this.tabs[i-1]);
22511     },
22512     
22513     
22514     addBullet: function()
22515     {
22516         if(!this.bullets || Roo.isTouch){
22517             return;
22518         }
22519         var ctr = this.el.select('.carousel-bullets',true).first();
22520         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22521         var bullet = ctr.createChild({
22522             cls : 'bullet bullet-' + i
22523         },ctr.dom.lastChild);
22524         
22525         
22526         var _this = this;
22527         
22528         bullet.on('click', (function(e, el, o, ii, t){
22529
22530             e.preventDefault();
22531
22532             this.showPanel(ii);
22533
22534             if(this.autoslide && this.slideFn){
22535                 clearInterval(this.slideFn);
22536                 this.slideFn = window.setInterval(function() {
22537                     _this.showPanelNext();
22538                 }, this.timer);
22539             }
22540
22541         }).createDelegate(this, [i, bullet], true));
22542                 
22543         
22544     },
22545      
22546     setActiveBullet : function(i)
22547     {
22548         if(Roo.isTouch){
22549             return;
22550         }
22551         
22552         Roo.each(this.el.select('.bullet', true).elements, function(el){
22553             el.removeClass('selected');
22554         });
22555
22556         var bullet = this.el.select('.bullet-' + i, true).first();
22557         
22558         if(!bullet){
22559             return;
22560         }
22561         
22562         bullet.addClass('selected');
22563     }
22564     
22565     
22566   
22567 });
22568
22569  
22570
22571  
22572  
22573 Roo.apply(Roo.bootstrap.TabGroup, {
22574     
22575     groups: {},
22576      /**
22577     * register a Navigation Group
22578     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22579     */
22580     register : function(navgrp)
22581     {
22582         this.groups[navgrp.navId] = navgrp;
22583         
22584     },
22585     /**
22586     * fetch a Navigation Group based on the navigation ID
22587     * if one does not exist , it will get created.
22588     * @param {string} the navgroup to add
22589     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22590     */
22591     get: function(navId) {
22592         if (typeof(this.groups[navId]) == 'undefined') {
22593             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22594         }
22595         return this.groups[navId] ;
22596     }
22597     
22598     
22599     
22600 });
22601
22602  /*
22603  * - LGPL
22604  *
22605  * TabPanel
22606  * 
22607  */
22608
22609 /**
22610  * @class Roo.bootstrap.TabPanel
22611  * @extends Roo.bootstrap.Component
22612  * @children Roo.bootstrap.Component
22613  * Bootstrap TabPanel class
22614  * @cfg {Boolean} active panel active
22615  * @cfg {String} html panel content
22616  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22617  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22618  * @cfg {String} href click to link..
22619  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22620  * 
22621  * 
22622  * @constructor
22623  * Create a new TabPanel
22624  * @param {Object} config The config object
22625  */
22626
22627 Roo.bootstrap.TabPanel = function(config){
22628     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22629     this.addEvents({
22630         /**
22631              * @event changed
22632              * Fires when the active status changes
22633              * @param {Roo.bootstrap.TabPanel} this
22634              * @param {Boolean} state the new state
22635             
22636          */
22637         'changed': true,
22638         /**
22639              * @event beforedeactivate
22640              * Fires before a tab is de-activated - can be used to do validation on a form.
22641              * @param {Roo.bootstrap.TabPanel} this
22642              * @return {Boolean} false if there is an error
22643             
22644          */
22645         'beforedeactivate': true
22646      });
22647     
22648     this.tabId = this.tabId || Roo.id();
22649   
22650 };
22651
22652 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22653     
22654     active: false,
22655     html: false,
22656     tabId: false,
22657     navId : false,
22658     href : '',
22659     touchSlide : false,
22660     getAutoCreate : function(){
22661         
22662         
22663         var cfg = {
22664             tag: 'div',
22665             // item is needed for carousel - not sure if it has any effect otherwise
22666             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22667             html: this.html || ''
22668         };
22669         
22670         if(this.active){
22671             cfg.cls += ' active';
22672         }
22673         
22674         if(this.tabId){
22675             cfg.tabId = this.tabId;
22676         }
22677         
22678         
22679         
22680         return cfg;
22681     },
22682     
22683     initEvents:  function()
22684     {
22685         var p = this.parent();
22686         
22687         this.navId = this.navId || p.navId;
22688         
22689         if (typeof(this.navId) != 'undefined') {
22690             // not really needed.. but just in case.. parent should be a NavGroup.
22691             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22692             
22693             tg.register(this);
22694             
22695             var i = tg.tabs.length - 1;
22696             
22697             if(this.active && tg.bullets > 0 && i < tg.bullets){
22698                 tg.setActiveBullet(i);
22699             }
22700         }
22701         
22702         this.el.on('click', this.onClick, this);
22703         
22704         if(Roo.isTouch && this.touchSlide){
22705             this.el.on("touchstart", this.onTouchStart, this);
22706             this.el.on("touchmove", this.onTouchMove, this);
22707             this.el.on("touchend", this.onTouchEnd, this);
22708         }
22709         
22710     },
22711     
22712     onRender : function(ct, position)
22713     {
22714         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22715     },
22716     
22717     setActive : function(state)
22718     {
22719         Roo.log("panel - set active " + this.tabId + "=" + state);
22720         
22721         this.active = state;
22722         if (!state) {
22723             this.el.removeClass('active');
22724             
22725         } else  if (!this.el.hasClass('active')) {
22726             this.el.addClass('active');
22727         }
22728         
22729         this.fireEvent('changed', this, state);
22730     },
22731     
22732     onClick : function(e)
22733     {
22734         e.preventDefault();
22735         
22736         if(!this.href.length){
22737             return;
22738         }
22739         
22740         window.location.href = this.href;
22741     },
22742     
22743     startX : 0,
22744     startY : 0,
22745     endX : 0,
22746     endY : 0,
22747     swiping : false,
22748     
22749     onTouchStart : function(e)
22750     {
22751         this.swiping = false;
22752         
22753         this.startX = e.browserEvent.touches[0].clientX;
22754         this.startY = e.browserEvent.touches[0].clientY;
22755     },
22756     
22757     onTouchMove : function(e)
22758     {
22759         this.swiping = true;
22760         
22761         this.endX = e.browserEvent.touches[0].clientX;
22762         this.endY = e.browserEvent.touches[0].clientY;
22763     },
22764     
22765     onTouchEnd : function(e)
22766     {
22767         if(!this.swiping){
22768             this.onClick(e);
22769             return;
22770         }
22771         
22772         var tabGroup = this.parent();
22773         
22774         if(this.endX > this.startX){ // swiping right
22775             tabGroup.showPanelPrev();
22776             return;
22777         }
22778         
22779         if(this.startX > this.endX){ // swiping left
22780             tabGroup.showPanelNext();
22781             return;
22782         }
22783     }
22784     
22785     
22786 });
22787  
22788
22789  
22790
22791  /*
22792  * - LGPL
22793  *
22794  * DateField
22795  * 
22796  */
22797
22798 /**
22799  * @class Roo.bootstrap.form.DateField
22800  * @extends Roo.bootstrap.form.Input
22801  * Bootstrap DateField class
22802  * @cfg {Number} weekStart default 0
22803  * @cfg {String} viewMode default empty, (months|years)
22804  * @cfg {String} minViewMode default empty, (months|years)
22805  * @cfg {Number} startDate default -Infinity
22806  * @cfg {Number} endDate default Infinity
22807  * @cfg {Boolean} todayHighlight default false
22808  * @cfg {Boolean} todayBtn default false
22809  * @cfg {Boolean} calendarWeeks default false
22810  * @cfg {Object} daysOfWeekDisabled default empty
22811  * @cfg {Boolean} singleMode default false (true | false)
22812  * 
22813  * @cfg {Boolean} keyboardNavigation default true
22814  * @cfg {String} language default en
22815  * 
22816  * @constructor
22817  * Create a new DateField
22818  * @param {Object} config The config object
22819  */
22820
22821 Roo.bootstrap.form.DateField = function(config){
22822     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22823      this.addEvents({
22824             /**
22825              * @event show
22826              * Fires when this field show.
22827              * @param {Roo.bootstrap.form.DateField} this
22828              * @param {Mixed} date The date value
22829              */
22830             show : true,
22831             /**
22832              * @event show
22833              * Fires when this field hide.
22834              * @param {Roo.bootstrap.form.DateField} this
22835              * @param {Mixed} date The date value
22836              */
22837             hide : true,
22838             /**
22839              * @event select
22840              * Fires when select a date.
22841              * @param {Roo.bootstrap.form.DateField} this
22842              * @param {Mixed} date The date value
22843              */
22844             select : true,
22845             /**
22846              * @event beforeselect
22847              * Fires when before select a date.
22848              * @param {Roo.bootstrap.form.DateField} this
22849              * @param {Mixed} date The date value
22850              */
22851             beforeselect : true
22852         });
22853 };
22854
22855 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22856     
22857     /**
22858      * @cfg {String} format
22859      * The default date format string which can be overriden for localization support.  The format must be
22860      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22861      */
22862     format : "m/d/y",
22863     /**
22864      * @cfg {String} altFormats
22865      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22866      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22867      */
22868     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22869     
22870     weekStart : 0,
22871     
22872     viewMode : '',
22873     
22874     minViewMode : '',
22875     
22876     todayHighlight : false,
22877     
22878     todayBtn: false,
22879     
22880     language: 'en',
22881     
22882     keyboardNavigation: true,
22883     
22884     calendarWeeks: false,
22885     
22886     startDate: -Infinity,
22887     
22888     endDate: Infinity,
22889     
22890     daysOfWeekDisabled: [],
22891     
22892     _events: [],
22893     
22894     singleMode : false,
22895     
22896     UTCDate: function()
22897     {
22898         return new Date(Date.UTC.apply(Date, arguments));
22899     },
22900     
22901     UTCToday: function()
22902     {
22903         var today = new Date();
22904         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22905     },
22906     
22907     getDate: function() {
22908             var d = this.getUTCDate();
22909             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22910     },
22911     
22912     getUTCDate: function() {
22913             return this.date;
22914     },
22915     
22916     setDate: function(d) {
22917             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22918     },
22919     
22920     setUTCDate: function(d) {
22921             this.date = d;
22922             this.setValue(this.formatDate(this.date));
22923     },
22924         
22925     onRender: function(ct, position)
22926     {
22927         
22928         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22929         
22930         this.language = this.language || 'en';
22931         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22932         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22933         
22934         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22935         this.format = this.format || 'm/d/y';
22936         this.isInline = false;
22937         this.isInput = true;
22938         this.component = this.el.select('.add-on', true).first() || false;
22939         this.component = (this.component && this.component.length === 0) ? false : this.component;
22940         this.hasInput = this.component && this.inputEl().length;
22941         
22942         if (typeof(this.minViewMode === 'string')) {
22943             switch (this.minViewMode) {
22944                 case 'months':
22945                     this.minViewMode = 1;
22946                     break;
22947                 case 'years':
22948                     this.minViewMode = 2;
22949                     break;
22950                 default:
22951                     this.minViewMode = 0;
22952                     break;
22953             }
22954         }
22955         
22956         if (typeof(this.viewMode === 'string')) {
22957             switch (this.viewMode) {
22958                 case 'months':
22959                     this.viewMode = 1;
22960                     break;
22961                 case 'years':
22962                     this.viewMode = 2;
22963                     break;
22964                 default:
22965                     this.viewMode = 0;
22966                     break;
22967             }
22968         }
22969                 
22970         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22971         
22972 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22973         
22974         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22975         
22976         this.picker().on('mousedown', this.onMousedown, this);
22977         this.picker().on('click', this.onClick, this);
22978         
22979         this.picker().addClass('datepicker-dropdown');
22980         
22981         this.startViewMode = this.viewMode;
22982         
22983         if(this.singleMode){
22984             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22985                 v.setVisibilityMode(Roo.Element.DISPLAY);
22986                 v.hide();
22987             });
22988             
22989             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22990                 v.setStyle('width', '189px');
22991             });
22992         }
22993         
22994         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22995             if(!this.calendarWeeks){
22996                 v.remove();
22997                 return;
22998             }
22999             
23000             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23001             v.attr('colspan', function(i, val){
23002                 return parseInt(val) + 1;
23003             });
23004         });
23005                         
23006         
23007         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23008         
23009         this.setStartDate(this.startDate);
23010         this.setEndDate(this.endDate);
23011         
23012         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23013         
23014         this.fillDow();
23015         this.fillMonths();
23016         this.update();
23017         this.showMode();
23018         
23019         if(this.isInline) {
23020             this.showPopup();
23021         }
23022     },
23023     
23024     picker : function()
23025     {
23026         return this.pickerEl;
23027 //        return this.el.select('.datepicker', true).first();
23028     },
23029     
23030     fillDow: function()
23031     {
23032         var dowCnt = this.weekStart;
23033         
23034         var dow = {
23035             tag: 'tr',
23036             cn: [
23037                 
23038             ]
23039         };
23040         
23041         if(this.calendarWeeks){
23042             dow.cn.push({
23043                 tag: 'th',
23044                 cls: 'cw',
23045                 html: '&nbsp;'
23046             })
23047         }
23048         
23049         while (dowCnt < this.weekStart + 7) {
23050             dow.cn.push({
23051                 tag: 'th',
23052                 cls: 'dow',
23053                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23054             });
23055         }
23056         
23057         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23058     },
23059     
23060     fillMonths: function()
23061     {    
23062         var i = 0;
23063         var months = this.picker().select('>.datepicker-months td', true).first();
23064         
23065         months.dom.innerHTML = '';
23066         
23067         while (i < 12) {
23068             var month = {
23069                 tag: 'span',
23070                 cls: 'month',
23071                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23072             };
23073             
23074             months.createChild(month);
23075         }
23076         
23077     },
23078     
23079     update: function()
23080     {
23081         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
23082         
23083         if (this.date < this.startDate) {
23084             this.viewDate = new Date(this.startDate);
23085         } else if (this.date > this.endDate) {
23086             this.viewDate = new Date(this.endDate);
23087         } else {
23088             this.viewDate = new Date(this.date);
23089         }
23090         
23091         this.fill();
23092     },
23093     
23094     fill: function() 
23095     {
23096         var d = new Date(this.viewDate),
23097                 year = d.getUTCFullYear(),
23098                 month = d.getUTCMonth(),
23099                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23100                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23101                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23102                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23103                 currentDate = this.date && this.date.valueOf(),
23104                 today = this.UTCToday();
23105         
23106         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23107         
23108 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23109         
23110 //        this.picker.select('>tfoot th.today').
23111 //                                              .text(dates[this.language].today)
23112 //                                              .toggle(this.todayBtn !== false);
23113     
23114         this.updateNavArrows();
23115         this.fillMonths();
23116                                                 
23117         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23118         
23119         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23120          
23121         prevMonth.setUTCDate(day);
23122         
23123         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23124         
23125         var nextMonth = new Date(prevMonth);
23126         
23127         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23128         
23129         nextMonth = nextMonth.valueOf();
23130         
23131         var fillMonths = false;
23132         
23133         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23134         
23135         while(prevMonth.valueOf() <= nextMonth) {
23136             var clsName = '';
23137             
23138             if (prevMonth.getUTCDay() === this.weekStart) {
23139                 if(fillMonths){
23140                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23141                 }
23142                     
23143                 fillMonths = {
23144                     tag: 'tr',
23145                     cn: []
23146                 };
23147                 
23148                 if(this.calendarWeeks){
23149                     // ISO 8601: First week contains first thursday.
23150                     // ISO also states week starts on Monday, but we can be more abstract here.
23151                     var
23152                     // Start of current week: based on weekstart/current date
23153                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23154                     // Thursday of this week
23155                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23156                     // First Thursday of year, year from thursday
23157                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23158                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23159                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23160                     
23161                     fillMonths.cn.push({
23162                         tag: 'td',
23163                         cls: 'cw',
23164                         html: calWeek
23165                     });
23166                 }
23167             }
23168             
23169             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23170                 clsName += ' old';
23171             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23172                 clsName += ' new';
23173             }
23174             if (this.todayHighlight &&
23175                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23176                 prevMonth.getUTCMonth() == today.getMonth() &&
23177                 prevMonth.getUTCDate() == today.getDate()) {
23178                 clsName += ' today';
23179             }
23180             
23181             if (currentDate && prevMonth.valueOf() === currentDate) {
23182                 clsName += ' active';
23183             }
23184             
23185             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23186                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23187                     clsName += ' disabled';
23188             }
23189             
23190             fillMonths.cn.push({
23191                 tag: 'td',
23192                 cls: 'day ' + clsName,
23193                 html: prevMonth.getDate()
23194             });
23195             
23196             prevMonth.setDate(prevMonth.getDate()+1);
23197         }
23198           
23199         var currentYear = this.date && this.date.getUTCFullYear();
23200         var currentMonth = this.date && this.date.getUTCMonth();
23201         
23202         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23203         
23204         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23205             v.removeClass('active');
23206             
23207             if(currentYear === year && k === currentMonth){
23208                 v.addClass('active');
23209             }
23210             
23211             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23212                 v.addClass('disabled');
23213             }
23214             
23215         });
23216         
23217         
23218         year = parseInt(year/10, 10) * 10;
23219         
23220         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23221         
23222         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23223         
23224         year -= 1;
23225         for (var i = -1; i < 11; i++) {
23226             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23227                 tag: 'span',
23228                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23229                 html: year
23230             });
23231             
23232             year += 1;
23233         }
23234     },
23235     
23236     showMode: function(dir) 
23237     {
23238         if (dir) {
23239             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23240         }
23241         
23242         Roo.each(this.picker().select('>div',true).elements, function(v){
23243             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23244             v.hide();
23245         });
23246         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23247     },
23248     
23249     place: function()
23250     {
23251         if(this.isInline) {
23252             return;
23253         }
23254         
23255         this.picker().removeClass(['bottom', 'top']);
23256         
23257         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23258             /*
23259              * place to the top of element!
23260              *
23261              */
23262             
23263             this.picker().addClass('top');
23264             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23265             
23266             return;
23267         }
23268         
23269         this.picker().addClass('bottom');
23270         
23271         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23272     },
23273     
23274     parseDate : function(value)
23275     {
23276         if(!value || value instanceof Date){
23277             return value;
23278         }
23279         var v = Date.parseDate(value, this.format);
23280         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23281             v = Date.parseDate(value, 'Y-m-d');
23282         }
23283         if(!v && this.altFormats){
23284             if(!this.altFormatsArray){
23285                 this.altFormatsArray = this.altFormats.split("|");
23286             }
23287             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23288                 v = Date.parseDate(value, this.altFormatsArray[i]);
23289             }
23290         }
23291         return v;
23292     },
23293     
23294     formatDate : function(date, fmt)
23295     {   
23296         return (!date || !(date instanceof Date)) ?
23297         date : date.dateFormat(fmt || this.format);
23298     },
23299     
23300     onFocus : function()
23301     {
23302         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23303         this.showPopup();
23304     },
23305     
23306     onBlur : function()
23307     {
23308         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23309         
23310         var d = this.inputEl().getValue();
23311         
23312         this.setValue(d);
23313                 
23314         this.hidePopup();
23315     },
23316     
23317     showPopup : function()
23318     {
23319         this.picker().show();
23320         this.update();
23321         this.place();
23322         
23323         this.fireEvent('showpopup', this, this.date);
23324     },
23325     
23326     hidePopup : function()
23327     {
23328         if(this.isInline) {
23329             return;
23330         }
23331         this.picker().hide();
23332         this.viewMode = this.startViewMode;
23333         this.showMode();
23334         
23335         this.fireEvent('hidepopup', this, this.date);
23336         
23337     },
23338     
23339     onMousedown: function(e)
23340     {
23341         e.stopPropagation();
23342         e.preventDefault();
23343     },
23344     
23345     keyup: function(e)
23346     {
23347         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23348         this.update();
23349     },
23350
23351     setValue: function(v)
23352     {
23353         if(this.fireEvent('beforeselect', this, v) !== false){
23354             var d = new Date(this.parseDate(v) ).clearTime();
23355         
23356             if(isNaN(d.getTime())){
23357                 this.date = this.viewDate = '';
23358                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23359                 return;
23360             }
23361
23362             v = this.formatDate(d);
23363
23364             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23365
23366             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23367
23368             this.update();
23369
23370             this.fireEvent('select', this, this.date);
23371         }
23372     },
23373     
23374     getValue: function()
23375     {
23376         return this.formatDate(this.date);
23377     },
23378     
23379     fireKey: function(e)
23380     {
23381         if (!this.picker().isVisible()){
23382             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23383                 this.showPopup();
23384             }
23385             return;
23386         }
23387         
23388         var dateChanged = false,
23389         dir, day, month,
23390         newDate, newViewDate;
23391         
23392         switch(e.keyCode){
23393             case 27: // escape
23394                 this.hidePopup();
23395                 e.preventDefault();
23396                 break;
23397             case 37: // left
23398             case 39: // right
23399                 if (!this.keyboardNavigation) {
23400                     break;
23401                 }
23402                 dir = e.keyCode == 37 ? -1 : 1;
23403                 
23404                 if (e.ctrlKey){
23405                     newDate = this.moveYear(this.date, dir);
23406                     newViewDate = this.moveYear(this.viewDate, dir);
23407                 } else if (e.shiftKey){
23408                     newDate = this.moveMonth(this.date, dir);
23409                     newViewDate = this.moveMonth(this.viewDate, dir);
23410                 } else {
23411                     newDate = new Date(this.date);
23412                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23413                     newViewDate = new Date(this.viewDate);
23414                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23415                 }
23416                 if (this.dateWithinRange(newDate)){
23417                     this.date = newDate;
23418                     this.viewDate = newViewDate;
23419                     this.setValue(this.formatDate(this.date));
23420 //                    this.update();
23421                     e.preventDefault();
23422                     dateChanged = true;
23423                 }
23424                 break;
23425             case 38: // up
23426             case 40: // down
23427                 if (!this.keyboardNavigation) {
23428                     break;
23429                 }
23430                 dir = e.keyCode == 38 ? -1 : 1;
23431                 if (e.ctrlKey){
23432                     newDate = this.moveYear(this.date, dir);
23433                     newViewDate = this.moveYear(this.viewDate, dir);
23434                 } else if (e.shiftKey){
23435                     newDate = this.moveMonth(this.date, dir);
23436                     newViewDate = this.moveMonth(this.viewDate, dir);
23437                 } else {
23438                     newDate = new Date(this.date);
23439                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23440                     newViewDate = new Date(this.viewDate);
23441                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23442                 }
23443                 if (this.dateWithinRange(newDate)){
23444                     this.date = newDate;
23445                     this.viewDate = newViewDate;
23446                     this.setValue(this.formatDate(this.date));
23447 //                    this.update();
23448                     e.preventDefault();
23449                     dateChanged = true;
23450                 }
23451                 break;
23452             case 13: // enter
23453                 this.setValue(this.formatDate(this.date));
23454                 this.hidePopup();
23455                 e.preventDefault();
23456                 break;
23457             case 9: // tab
23458                 this.setValue(this.formatDate(this.date));
23459                 this.hidePopup();
23460                 break;
23461             case 16: // shift
23462             case 17: // ctrl
23463             case 18: // alt
23464                 break;
23465             default :
23466                 this.hidePopup();
23467                 
23468         }
23469     },
23470     
23471     
23472     onClick: function(e) 
23473     {
23474         e.stopPropagation();
23475         e.preventDefault();
23476         
23477         var target = e.getTarget();
23478         
23479         if(target.nodeName.toLowerCase() === 'i'){
23480             target = Roo.get(target).dom.parentNode;
23481         }
23482         
23483         var nodeName = target.nodeName;
23484         var className = target.className;
23485         var html = target.innerHTML;
23486         //Roo.log(nodeName);
23487         
23488         switch(nodeName.toLowerCase()) {
23489             case 'th':
23490                 switch(className) {
23491                     case 'switch':
23492                         this.showMode(1);
23493                         break;
23494                     case 'prev':
23495                     case 'next':
23496                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23497                         switch(this.viewMode){
23498                                 case 0:
23499                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23500                                         break;
23501                                 case 1:
23502                                 case 2:
23503                                         this.viewDate = this.moveYear(this.viewDate, dir);
23504                                         break;
23505                         }
23506                         this.fill();
23507                         break;
23508                     case 'today':
23509                         var date = new Date();
23510                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23511 //                        this.fill()
23512                         this.setValue(this.formatDate(this.date));
23513                         
23514                         this.hidePopup();
23515                         break;
23516                 }
23517                 break;
23518             case 'span':
23519                 if (className.indexOf('disabled') < 0) {
23520                 if (!this.viewDate) {
23521                     this.viewDate = new Date();
23522                 }
23523                 this.viewDate.setUTCDate(1);
23524                     if (className.indexOf('month') > -1) {
23525                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23526                     } else {
23527                         var year = parseInt(html, 10) || 0;
23528                         this.viewDate.setUTCFullYear(year);
23529                         
23530                     }
23531                     
23532                     if(this.singleMode){
23533                         this.setValue(this.formatDate(this.viewDate));
23534                         this.hidePopup();
23535                         return;
23536                     }
23537                     
23538                     this.showMode(-1);
23539                     this.fill();
23540                 }
23541                 break;
23542                 
23543             case 'td':
23544                 //Roo.log(className);
23545                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23546                     var day = parseInt(html, 10) || 1;
23547                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23548                         month = (this.viewDate || new Date()).getUTCMonth();
23549
23550                     if (className.indexOf('old') > -1) {
23551                         if(month === 0 ){
23552                             month = 11;
23553                             year -= 1;
23554                         }else{
23555                             month -= 1;
23556                         }
23557                     } else if (className.indexOf('new') > -1) {
23558                         if (month == 11) {
23559                             month = 0;
23560                             year += 1;
23561                         } else {
23562                             month += 1;
23563                         }
23564                     }
23565                     //Roo.log([year,month,day]);
23566                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23567                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23568 //                    this.fill();
23569                     //Roo.log(this.formatDate(this.date));
23570                     this.setValue(this.formatDate(this.date));
23571                     this.hidePopup();
23572                 }
23573                 break;
23574         }
23575     },
23576     
23577     setStartDate: function(startDate)
23578     {
23579         this.startDate = startDate || -Infinity;
23580         if (this.startDate !== -Infinity) {
23581             this.startDate = this.parseDate(this.startDate);
23582         }
23583         this.update();
23584         this.updateNavArrows();
23585     },
23586
23587     setEndDate: function(endDate)
23588     {
23589         this.endDate = endDate || Infinity;
23590         if (this.endDate !== Infinity) {
23591             this.endDate = this.parseDate(this.endDate);
23592         }
23593         this.update();
23594         this.updateNavArrows();
23595     },
23596     
23597     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23598     {
23599         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23600         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23601             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23602         }
23603         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23604             return parseInt(d, 10);
23605         });
23606         this.update();
23607         this.updateNavArrows();
23608     },
23609     
23610     updateNavArrows: function() 
23611     {
23612         if(this.singleMode){
23613             return;
23614         }
23615         
23616         var d = new Date(this.viewDate),
23617         year = d.getUTCFullYear(),
23618         month = d.getUTCMonth();
23619         
23620         Roo.each(this.picker().select('.prev', true).elements, function(v){
23621             v.show();
23622             switch (this.viewMode) {
23623                 case 0:
23624
23625                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23626                         v.hide();
23627                     }
23628                     break;
23629                 case 1:
23630                 case 2:
23631                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23632                         v.hide();
23633                     }
23634                     break;
23635             }
23636         });
23637         
23638         Roo.each(this.picker().select('.next', true).elements, function(v){
23639             v.show();
23640             switch (this.viewMode) {
23641                 case 0:
23642
23643                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23644                         v.hide();
23645                     }
23646                     break;
23647                 case 1:
23648                 case 2:
23649                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23650                         v.hide();
23651                     }
23652                     break;
23653             }
23654         })
23655     },
23656     
23657     moveMonth: function(date, dir)
23658     {
23659         if (!dir) {
23660             return date;
23661         }
23662         var new_date = new Date(date.valueOf()),
23663         day = new_date.getUTCDate(),
23664         month = new_date.getUTCMonth(),
23665         mag = Math.abs(dir),
23666         new_month, test;
23667         dir = dir > 0 ? 1 : -1;
23668         if (mag == 1){
23669             test = dir == -1
23670             // If going back one month, make sure month is not current month
23671             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23672             ? function(){
23673                 return new_date.getUTCMonth() == month;
23674             }
23675             // If going forward one month, make sure month is as expected
23676             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23677             : function(){
23678                 return new_date.getUTCMonth() != new_month;
23679             };
23680             new_month = month + dir;
23681             new_date.setUTCMonth(new_month);
23682             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23683             if (new_month < 0 || new_month > 11) {
23684                 new_month = (new_month + 12) % 12;
23685             }
23686         } else {
23687             // For magnitudes >1, move one month at a time...
23688             for (var i=0; i<mag; i++) {
23689                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23690                 new_date = this.moveMonth(new_date, dir);
23691             }
23692             // ...then reset the day, keeping it in the new month
23693             new_month = new_date.getUTCMonth();
23694             new_date.setUTCDate(day);
23695             test = function(){
23696                 return new_month != new_date.getUTCMonth();
23697             };
23698         }
23699         // Common date-resetting loop -- if date is beyond end of month, make it
23700         // end of month
23701         while (test()){
23702             new_date.setUTCDate(--day);
23703             new_date.setUTCMonth(new_month);
23704         }
23705         return new_date;
23706     },
23707
23708     moveYear: function(date, dir)
23709     {
23710         return this.moveMonth(date, dir*12);
23711     },
23712
23713     dateWithinRange: function(date)
23714     {
23715         return date >= this.startDate && date <= this.endDate;
23716     },
23717
23718     
23719     remove: function() 
23720     {
23721         this.picker().remove();
23722     },
23723     
23724     validateValue : function(value)
23725     {
23726         if(this.getVisibilityEl().hasClass('hidden')){
23727             return true;
23728         }
23729         
23730         if(value.length < 1)  {
23731             if(this.allowBlank){
23732                 return true;
23733             }
23734             return false;
23735         }
23736         
23737         if(value.length < this.minLength){
23738             return false;
23739         }
23740         if(value.length > this.maxLength){
23741             return false;
23742         }
23743         if(this.vtype){
23744             var vt = Roo.form.VTypes;
23745             if(!vt[this.vtype](value, this)){
23746                 return false;
23747             }
23748         }
23749         if(typeof this.validator == "function"){
23750             var msg = this.validator(value);
23751             if(msg !== true){
23752                 return false;
23753             }
23754         }
23755         
23756         if(this.regex && !this.regex.test(value)){
23757             return false;
23758         }
23759         
23760         if(typeof(this.parseDate(value)) == 'undefined'){
23761             return false;
23762         }
23763         
23764         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23765             return false;
23766         }      
23767         
23768         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23769             return false;
23770         } 
23771         
23772         
23773         return true;
23774     },
23775     
23776     reset : function()
23777     {
23778         this.date = this.viewDate = '';
23779         
23780         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23781     }
23782    
23783 });
23784
23785 Roo.apply(Roo.bootstrap.form.DateField,  {
23786     
23787     head : {
23788         tag: 'thead',
23789         cn: [
23790         {
23791             tag: 'tr',
23792             cn: [
23793             {
23794                 tag: 'th',
23795                 cls: 'prev',
23796                 html: '<i class="fa fa-arrow-left"/>'
23797             },
23798             {
23799                 tag: 'th',
23800                 cls: 'switch',
23801                 colspan: '5'
23802             },
23803             {
23804                 tag: 'th',
23805                 cls: 'next',
23806                 html: '<i class="fa fa-arrow-right"/>'
23807             }
23808
23809             ]
23810         }
23811         ]
23812     },
23813     
23814     content : {
23815         tag: 'tbody',
23816         cn: [
23817         {
23818             tag: 'tr',
23819             cn: [
23820             {
23821                 tag: 'td',
23822                 colspan: '7'
23823             }
23824             ]
23825         }
23826         ]
23827     },
23828     
23829     footer : {
23830         tag: 'tfoot',
23831         cn: [
23832         {
23833             tag: 'tr',
23834             cn: [
23835             {
23836                 tag: 'th',
23837                 colspan: '7',
23838                 cls: 'today'
23839             }
23840                     
23841             ]
23842         }
23843         ]
23844     },
23845     
23846     dates:{
23847         en: {
23848             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23849             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23850             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23851             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23852             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23853             today: "Today"
23854         }
23855     },
23856     
23857     modes: [
23858     {
23859         clsName: 'days',
23860         navFnc: 'Month',
23861         navStep: 1
23862     },
23863     {
23864         clsName: 'months',
23865         navFnc: 'FullYear',
23866         navStep: 1
23867     },
23868     {
23869         clsName: 'years',
23870         navFnc: 'FullYear',
23871         navStep: 10
23872     }]
23873 });
23874
23875 Roo.apply(Roo.bootstrap.form.DateField,  {
23876   
23877     template : {
23878         tag: 'div',
23879         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23880         cn: [
23881         {
23882             tag: 'div',
23883             cls: 'datepicker-days',
23884             cn: [
23885             {
23886                 tag: 'table',
23887                 cls: 'table-condensed',
23888                 cn:[
23889                 Roo.bootstrap.form.DateField.head,
23890                 {
23891                     tag: 'tbody'
23892                 },
23893                 Roo.bootstrap.form.DateField.footer
23894                 ]
23895             }
23896             ]
23897         },
23898         {
23899             tag: 'div',
23900             cls: 'datepicker-months',
23901             cn: [
23902             {
23903                 tag: 'table',
23904                 cls: 'table-condensed',
23905                 cn:[
23906                 Roo.bootstrap.form.DateField.head,
23907                 Roo.bootstrap.form.DateField.content,
23908                 Roo.bootstrap.form.DateField.footer
23909                 ]
23910             }
23911             ]
23912         },
23913         {
23914             tag: 'div',
23915             cls: 'datepicker-years',
23916             cn: [
23917             {
23918                 tag: 'table',
23919                 cls: 'table-condensed',
23920                 cn:[
23921                 Roo.bootstrap.form.DateField.head,
23922                 Roo.bootstrap.form.DateField.content,
23923                 Roo.bootstrap.form.DateField.footer
23924                 ]
23925             }
23926             ]
23927         }
23928         ]
23929     }
23930 });
23931
23932  
23933
23934  /*
23935  * - LGPL
23936  *
23937  * TimeField
23938  * 
23939  */
23940
23941 /**
23942  * @class Roo.bootstrap.form.TimeField
23943  * @extends Roo.bootstrap.form.Input
23944  * Bootstrap DateField class
23945  * 
23946  * 
23947  * @constructor
23948  * Create a new TimeField
23949  * @param {Object} config The config object
23950  */
23951
23952 Roo.bootstrap.form.TimeField = function(config){
23953     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23954     this.addEvents({
23955             /**
23956              * @event show
23957              * Fires when this field show.
23958              * @param {Roo.bootstrap.form.DateField} thisthis
23959              * @param {Mixed} date The date value
23960              */
23961             show : true,
23962             /**
23963              * @event show
23964              * Fires when this field hide.
23965              * @param {Roo.bootstrap.form.DateField} this
23966              * @param {Mixed} date The date value
23967              */
23968             hide : true,
23969             /**
23970              * @event select
23971              * Fires when select a date.
23972              * @param {Roo.bootstrap.form.DateField} this
23973              * @param {Mixed} date The date value
23974              */
23975             select : true
23976         });
23977 };
23978
23979 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23980     
23981     /**
23982      * @cfg {String} format
23983      * The default time format string which can be overriden for localization support.  The format must be
23984      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23985      */
23986     format : "H:i",
23987
23988     getAutoCreate : function()
23989     {
23990         this.after = '<i class="fa far fa-clock"></i>';
23991         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23992         
23993          
23994     },
23995     onRender: function(ct, position)
23996     {
23997         
23998         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23999                 
24000         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24001         
24002         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24003         
24004         this.pop = this.picker().select('>.datepicker-time',true).first();
24005         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24006         
24007         this.picker().on('mousedown', this.onMousedown, this);
24008         this.picker().on('click', this.onClick, this);
24009         
24010         this.picker().addClass('datepicker-dropdown');
24011     
24012         this.fillTime();
24013         this.update();
24014             
24015         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24016         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24017         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24018         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24019         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24020         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24021
24022     },
24023     
24024     fireKey: function(e){
24025         if (!this.picker().isVisible()){
24026             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24027                 this.show();
24028             }
24029             return;
24030         }
24031
24032         e.preventDefault();
24033         
24034         switch(e.keyCode){
24035             case 27: // escape
24036                 this.hide();
24037                 break;
24038             case 37: // left
24039             case 39: // right
24040                 this.onTogglePeriod();
24041                 break;
24042             case 38: // up
24043                 this.onIncrementMinutes();
24044                 break;
24045             case 40: // down
24046                 this.onDecrementMinutes();
24047                 break;
24048             case 13: // enter
24049             case 9: // tab
24050                 this.setTime();
24051                 break;
24052         }
24053     },
24054     
24055     onClick: function(e) {
24056         e.stopPropagation();
24057         e.preventDefault();
24058     },
24059     
24060     picker : function()
24061     {
24062         return this.pickerEl;
24063     },
24064     
24065     fillTime: function()
24066     {    
24067         var time = this.pop.select('tbody', true).first();
24068         
24069         time.dom.innerHTML = '';
24070         
24071         time.createChild({
24072             tag: 'tr',
24073             cn: [
24074                 {
24075                     tag: 'td',
24076                     cn: [
24077                         {
24078                             tag: 'a',
24079                             href: '#',
24080                             cls: 'btn',
24081                             cn: [
24082                                 {
24083                                     tag: 'i',
24084                                     cls: 'hours-up fa fas fa-chevron-up'
24085                                 }
24086                             ]
24087                         } 
24088                     ]
24089                 },
24090                 {
24091                     tag: 'td',
24092                     cls: 'separator'
24093                 },
24094                 {
24095                     tag: 'td',
24096                     cn: [
24097                         {
24098                             tag: 'a',
24099                             href: '#',
24100                             cls: 'btn',
24101                             cn: [
24102                                 {
24103                                     tag: 'i',
24104                                     cls: 'minutes-up fa fas fa-chevron-up'
24105                                 }
24106                             ]
24107                         }
24108                     ]
24109                 },
24110                 {
24111                     tag: 'td',
24112                     cls: 'separator'
24113                 }
24114             ]
24115         });
24116         
24117         time.createChild({
24118             tag: 'tr',
24119             cn: [
24120                 {
24121                     tag: 'td',
24122                     cn: [
24123                         {
24124                             tag: 'span',
24125                             cls: 'timepicker-hour',
24126                             html: '00'
24127                         }  
24128                     ]
24129                 },
24130                 {
24131                     tag: 'td',
24132                     cls: 'separator',
24133                     html: ':'
24134                 },
24135                 {
24136                     tag: 'td',
24137                     cn: [
24138                         {
24139                             tag: 'span',
24140                             cls: 'timepicker-minute',
24141                             html: '00'
24142                         }  
24143                     ]
24144                 },
24145                 {
24146                     tag: 'td',
24147                     cls: 'separator'
24148                 },
24149                 {
24150                     tag: 'td',
24151                     cn: [
24152                         {
24153                             tag: 'button',
24154                             type: 'button',
24155                             cls: 'btn btn-primary period',
24156                             html: 'AM'
24157                             
24158                         }
24159                     ]
24160                 }
24161             ]
24162         });
24163         
24164         time.createChild({
24165             tag: 'tr',
24166             cn: [
24167                 {
24168                     tag: 'td',
24169                     cn: [
24170                         {
24171                             tag: 'a',
24172                             href: '#',
24173                             cls: 'btn',
24174                             cn: [
24175                                 {
24176                                     tag: 'span',
24177                                     cls: 'hours-down fa fas fa-chevron-down'
24178                                 }
24179                             ]
24180                         }
24181                     ]
24182                 },
24183                 {
24184                     tag: 'td',
24185                     cls: 'separator'
24186                 },
24187                 {
24188                     tag: 'td',
24189                     cn: [
24190                         {
24191                             tag: 'a',
24192                             href: '#',
24193                             cls: 'btn',
24194                             cn: [
24195                                 {
24196                                     tag: 'span',
24197                                     cls: 'minutes-down fa fas fa-chevron-down'
24198                                 }
24199                             ]
24200                         }
24201                     ]
24202                 },
24203                 {
24204                     tag: 'td',
24205                     cls: 'separator'
24206                 }
24207             ]
24208         });
24209         
24210     },
24211     
24212     update: function()
24213     {
24214         
24215         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24216         
24217         this.fill();
24218     },
24219     
24220     fill: function() 
24221     {
24222         var hours = this.time.getHours();
24223         var minutes = this.time.getMinutes();
24224         var period = 'AM';
24225         
24226         if(hours > 11){
24227             period = 'PM';
24228         }
24229         
24230         if(hours == 0){
24231             hours = 12;
24232         }
24233         
24234         
24235         if(hours > 12){
24236             hours = hours - 12;
24237         }
24238         
24239         if(hours < 10){
24240             hours = '0' + hours;
24241         }
24242         
24243         if(minutes < 10){
24244             minutes = '0' + minutes;
24245         }
24246         
24247         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24248         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24249         this.pop.select('button', true).first().dom.innerHTML = period;
24250         
24251     },
24252     
24253     place: function()
24254     {   
24255         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24256         
24257         var cls = ['bottom'];
24258         
24259         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24260             cls.pop();
24261             cls.push('top');
24262         }
24263         
24264         cls.push('right');
24265         
24266         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24267             cls.pop();
24268             cls.push('left');
24269         }
24270         //this.picker().setXY(20000,20000);
24271         this.picker().addClass(cls.join('-'));
24272         
24273         var _this = this;
24274         
24275         Roo.each(cls, function(c){
24276             if(c == 'bottom'){
24277                 (function() {
24278                  //  
24279                 }).defer(200);
24280                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24281                 //_this.picker().setTop(_this.inputEl().getHeight());
24282                 return;
24283             }
24284             if(c == 'top'){
24285                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24286                 
24287                 //_this.picker().setTop(0 - _this.picker().getHeight());
24288                 return;
24289             }
24290             /*
24291             if(c == 'left'){
24292                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24293                 return;
24294             }
24295             if(c == 'right'){
24296                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24297                 return;
24298             }
24299             */
24300         });
24301         
24302     },
24303   
24304     onFocus : function()
24305     {
24306         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24307         this.show();
24308     },
24309     
24310     onBlur : function()
24311     {
24312         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24313         this.hide();
24314     },
24315     
24316     show : function()
24317     {
24318         this.picker().show();
24319         this.pop.show();
24320         this.update();
24321         this.place();
24322         
24323         this.fireEvent('show', this, this.date);
24324     },
24325     
24326     hide : function()
24327     {
24328         this.picker().hide();
24329         this.pop.hide();
24330         
24331         this.fireEvent('hide', this, this.date);
24332     },
24333     
24334     setTime : function()
24335     {
24336         this.hide();
24337         this.setValue(this.time.format(this.format));
24338         
24339         this.fireEvent('select', this, this.date);
24340         
24341         
24342     },
24343     
24344     onMousedown: function(e){
24345         e.stopPropagation();
24346         e.preventDefault();
24347     },
24348     
24349     onIncrementHours: function()
24350     {
24351         Roo.log('onIncrementHours');
24352         this.time = this.time.add(Date.HOUR, 1);
24353         this.update();
24354         
24355     },
24356     
24357     onDecrementHours: function()
24358     {
24359         Roo.log('onDecrementHours');
24360         this.time = this.time.add(Date.HOUR, -1);
24361         this.update();
24362     },
24363     
24364     onIncrementMinutes: function()
24365     {
24366         Roo.log('onIncrementMinutes');
24367         this.time = this.time.add(Date.MINUTE, 1);
24368         this.update();
24369     },
24370     
24371     onDecrementMinutes: function()
24372     {
24373         Roo.log('onDecrementMinutes');
24374         this.time = this.time.add(Date.MINUTE, -1);
24375         this.update();
24376     },
24377     
24378     onTogglePeriod: function()
24379     {
24380         Roo.log('onTogglePeriod');
24381         this.time = this.time.add(Date.HOUR, 12);
24382         this.update();
24383     }
24384     
24385    
24386 });
24387  
24388
24389 Roo.apply(Roo.bootstrap.form.TimeField,  {
24390   
24391     template : {
24392         tag: 'div',
24393         cls: 'datepicker dropdown-menu',
24394         cn: [
24395             {
24396                 tag: 'div',
24397                 cls: 'datepicker-time',
24398                 cn: [
24399                 {
24400                     tag: 'table',
24401                     cls: 'table-condensed',
24402                     cn:[
24403                         {
24404                             tag: 'tbody',
24405                             cn: [
24406                                 {
24407                                     tag: 'tr',
24408                                     cn: [
24409                                     {
24410                                         tag: 'td',
24411                                         colspan: '7'
24412                                     }
24413                                     ]
24414                                 }
24415                             ]
24416                         },
24417                         {
24418                             tag: 'tfoot',
24419                             cn: [
24420                                 {
24421                                     tag: 'tr',
24422                                     cn: [
24423                                     {
24424                                         tag: 'th',
24425                                         colspan: '7',
24426                                         cls: '',
24427                                         cn: [
24428                                             {
24429                                                 tag: 'button',
24430                                                 cls: 'btn btn-info ok',
24431                                                 html: 'OK'
24432                                             }
24433                                         ]
24434                                     }
24435                     
24436                                     ]
24437                                 }
24438                             ]
24439                         }
24440                     ]
24441                 }
24442                 ]
24443             }
24444         ]
24445     }
24446 });
24447
24448  
24449
24450  /*
24451  * - LGPL
24452  *
24453  * MonthField
24454  * 
24455  */
24456
24457 /**
24458  * @class Roo.bootstrap.form.MonthField
24459  * @extends Roo.bootstrap.form.Input
24460  * Bootstrap MonthField class
24461  * 
24462  * @cfg {String} language default en
24463  * 
24464  * @constructor
24465  * Create a new MonthField
24466  * @param {Object} config The config object
24467  */
24468
24469 Roo.bootstrap.form.MonthField = function(config){
24470     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24471     
24472     this.addEvents({
24473         /**
24474          * @event show
24475          * Fires when this field show.
24476          * @param {Roo.bootstrap.form.MonthField} this
24477          * @param {Mixed} date The date value
24478          */
24479         show : true,
24480         /**
24481          * @event show
24482          * Fires when this field hide.
24483          * @param {Roo.bootstrap.form.MonthField} this
24484          * @param {Mixed} date The date value
24485          */
24486         hide : true,
24487         /**
24488          * @event select
24489          * Fires when select a date.
24490          * @param {Roo.bootstrap.form.MonthField} this
24491          * @param {String} oldvalue The old value
24492          * @param {String} newvalue The new value
24493          */
24494         select : true
24495     });
24496 };
24497
24498 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24499     
24500     onRender: function(ct, position)
24501     {
24502         
24503         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24504         
24505         this.language = this.language || 'en';
24506         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24507         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24508         
24509         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24510         this.isInline = false;
24511         this.isInput = true;
24512         this.component = this.el.select('.add-on', true).first() || false;
24513         this.component = (this.component && this.component.length === 0) ? false : this.component;
24514         this.hasInput = this.component && this.inputEL().length;
24515         
24516         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24517         
24518         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24519         
24520         this.picker().on('mousedown', this.onMousedown, this);
24521         this.picker().on('click', this.onClick, this);
24522         
24523         this.picker().addClass('datepicker-dropdown');
24524         
24525         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24526             v.setStyle('width', '189px');
24527         });
24528         
24529         this.fillMonths();
24530         
24531         this.update();
24532         
24533         if(this.isInline) {
24534             this.show();
24535         }
24536         
24537     },
24538     
24539     setValue: function(v, suppressEvent)
24540     {   
24541         var o = this.getValue();
24542         
24543         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24544         
24545         this.update();
24546
24547         if(suppressEvent !== true){
24548             this.fireEvent('select', this, o, v);
24549         }
24550         
24551     },
24552     
24553     getValue: function()
24554     {
24555         return this.value;
24556     },
24557     
24558     onClick: function(e) 
24559     {
24560         e.stopPropagation();
24561         e.preventDefault();
24562         
24563         var target = e.getTarget();
24564         
24565         if(target.nodeName.toLowerCase() === 'i'){
24566             target = Roo.get(target).dom.parentNode;
24567         }
24568         
24569         var nodeName = target.nodeName;
24570         var className = target.className;
24571         var html = target.innerHTML;
24572         
24573         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24574             return;
24575         }
24576         
24577         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24578         
24579         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24580         
24581         this.hide();
24582                         
24583     },
24584     
24585     picker : function()
24586     {
24587         return this.pickerEl;
24588     },
24589     
24590     fillMonths: function()
24591     {    
24592         var i = 0;
24593         var months = this.picker().select('>.datepicker-months td', true).first();
24594         
24595         months.dom.innerHTML = '';
24596         
24597         while (i < 12) {
24598             var month = {
24599                 tag: 'span',
24600                 cls: 'month',
24601                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24602             };
24603             
24604             months.createChild(month);
24605         }
24606         
24607     },
24608     
24609     update: function()
24610     {
24611         var _this = this;
24612         
24613         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24614             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24615         }
24616         
24617         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24618             e.removeClass('active');
24619             
24620             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24621                 e.addClass('active');
24622             }
24623         })
24624     },
24625     
24626     place: function()
24627     {
24628         if(this.isInline) {
24629             return;
24630         }
24631         
24632         this.picker().removeClass(['bottom', 'top']);
24633         
24634         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24635             /*
24636              * place to the top of element!
24637              *
24638              */
24639             
24640             this.picker().addClass('top');
24641             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24642             
24643             return;
24644         }
24645         
24646         this.picker().addClass('bottom');
24647         
24648         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24649     },
24650     
24651     onFocus : function()
24652     {
24653         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24654         this.show();
24655     },
24656     
24657     onBlur : function()
24658     {
24659         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24660         
24661         var d = this.inputEl().getValue();
24662         
24663         this.setValue(d);
24664                 
24665         this.hide();
24666     },
24667     
24668     show : function()
24669     {
24670         this.picker().show();
24671         this.picker().select('>.datepicker-months', true).first().show();
24672         this.update();
24673         this.place();
24674         
24675         this.fireEvent('show', this, this.date);
24676     },
24677     
24678     hide : function()
24679     {
24680         if(this.isInline) {
24681             return;
24682         }
24683         this.picker().hide();
24684         this.fireEvent('hide', this, this.date);
24685         
24686     },
24687     
24688     onMousedown: function(e)
24689     {
24690         e.stopPropagation();
24691         e.preventDefault();
24692     },
24693     
24694     keyup: function(e)
24695     {
24696         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24697         this.update();
24698     },
24699
24700     fireKey: function(e)
24701     {
24702         if (!this.picker().isVisible()){
24703             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24704                 this.show();
24705             }
24706             return;
24707         }
24708         
24709         var dir;
24710         
24711         switch(e.keyCode){
24712             case 27: // escape
24713                 this.hide();
24714                 e.preventDefault();
24715                 break;
24716             case 37: // left
24717             case 39: // right
24718                 dir = e.keyCode == 37 ? -1 : 1;
24719                 
24720                 this.vIndex = this.vIndex + dir;
24721                 
24722                 if(this.vIndex < 0){
24723                     this.vIndex = 0;
24724                 }
24725                 
24726                 if(this.vIndex > 11){
24727                     this.vIndex = 11;
24728                 }
24729                 
24730                 if(isNaN(this.vIndex)){
24731                     this.vIndex = 0;
24732                 }
24733                 
24734                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24735                 
24736                 break;
24737             case 38: // up
24738             case 40: // down
24739                 
24740                 dir = e.keyCode == 38 ? -1 : 1;
24741                 
24742                 this.vIndex = this.vIndex + dir * 4;
24743                 
24744                 if(this.vIndex < 0){
24745                     this.vIndex = 0;
24746                 }
24747                 
24748                 if(this.vIndex > 11){
24749                     this.vIndex = 11;
24750                 }
24751                 
24752                 if(isNaN(this.vIndex)){
24753                     this.vIndex = 0;
24754                 }
24755                 
24756                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24757                 break;
24758                 
24759             case 13: // enter
24760                 
24761                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24762                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24763                 }
24764                 
24765                 this.hide();
24766                 e.preventDefault();
24767                 break;
24768             case 9: // tab
24769                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24770                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24771                 }
24772                 this.hide();
24773                 break;
24774             case 16: // shift
24775             case 17: // ctrl
24776             case 18: // alt
24777                 break;
24778             default :
24779                 this.hide();
24780                 
24781         }
24782     },
24783     
24784     remove: function() 
24785     {
24786         this.picker().remove();
24787     }
24788    
24789 });
24790
24791 Roo.apply(Roo.bootstrap.form.MonthField,  {
24792     
24793     content : {
24794         tag: 'tbody',
24795         cn: [
24796         {
24797             tag: 'tr',
24798             cn: [
24799             {
24800                 tag: 'td',
24801                 colspan: '7'
24802             }
24803             ]
24804         }
24805         ]
24806     },
24807     
24808     dates:{
24809         en: {
24810             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24811             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24812         }
24813     }
24814 });
24815
24816 Roo.apply(Roo.bootstrap.form.MonthField,  {
24817   
24818     template : {
24819         tag: 'div',
24820         cls: 'datepicker dropdown-menu roo-dynamic',
24821         cn: [
24822             {
24823                 tag: 'div',
24824                 cls: 'datepicker-months',
24825                 cn: [
24826                 {
24827                     tag: 'table',
24828                     cls: 'table-condensed',
24829                     cn:[
24830                         Roo.bootstrap.form.DateField.content
24831                     ]
24832                 }
24833                 ]
24834             }
24835         ]
24836     }
24837 });
24838
24839  
24840
24841  
24842  /*
24843  * - LGPL
24844  *
24845  * CheckBox
24846  * 
24847  */
24848
24849 /**
24850  * @class Roo.bootstrap.form.CheckBox
24851  * @extends Roo.bootstrap.form.Input
24852  * Bootstrap CheckBox class
24853  * 
24854  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24855  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24856  * @cfg {String} boxLabel The text that appears beside the checkbox
24857  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24858  * @cfg {Boolean} checked initnal the element
24859  * @cfg {Boolean} inline inline the element (default false)
24860  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24861  * @cfg {String} tooltip label tooltip
24862  * 
24863  * @constructor
24864  * Create a new CheckBox
24865  * @param {Object} config The config object
24866  */
24867
24868 Roo.bootstrap.form.CheckBox = function(config){
24869     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24870    
24871     this.addEvents({
24872         /**
24873         * @event check
24874         * Fires when the element is checked or unchecked.
24875         * @param {Roo.bootstrap.form.CheckBox} this This input
24876         * @param {Boolean} checked The new checked value
24877         */
24878        check : true,
24879        /**
24880         * @event click
24881         * Fires when the element is click.
24882         * @param {Roo.bootstrap.form.CheckBox} this This input
24883         */
24884        click : true
24885     });
24886     
24887 };
24888
24889 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24890   
24891     inputType: 'checkbox',
24892     inputValue: 1,
24893     valueOff: 0,
24894     boxLabel: false,
24895     checked: false,
24896     weight : false,
24897     inline: false,
24898     tooltip : '',
24899     
24900     // checkbox success does not make any sense really.. 
24901     invalidClass : "",
24902     validClass : "",
24903     
24904     
24905     getAutoCreate : function()
24906     {
24907         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24908         
24909         var id = Roo.id();
24910         
24911         var cfg = {};
24912         
24913         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24914         
24915         if(this.inline){
24916             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24917         }
24918         
24919         var input =  {
24920             tag: 'input',
24921             id : id,
24922             type : this.inputType,
24923             value : this.inputValue,
24924             cls : 'roo-' + this.inputType, //'form-box',
24925             placeholder : this.placeholder || ''
24926             
24927         };
24928         
24929         if(this.inputType != 'radio'){
24930             var hidden =  {
24931                 tag: 'input',
24932                 type : 'hidden',
24933                 cls : 'roo-hidden-value',
24934                 value : this.checked ? this.inputValue : this.valueOff
24935             };
24936         }
24937         
24938             
24939         if (this.weight) { // Validity check?
24940             cfg.cls += " " + this.inputType + "-" + this.weight;
24941         }
24942         
24943         if (this.disabled) {
24944             input.disabled=true;
24945         }
24946         
24947         if(this.checked){
24948             input.checked = this.checked;
24949         }
24950         
24951         if (this.name) {
24952             
24953             input.name = this.name;
24954             
24955             if(this.inputType != 'radio'){
24956                 hidden.name = this.name;
24957                 input.name = '_hidden_' + this.name;
24958             }
24959         }
24960         
24961         if (this.size) {
24962             input.cls += ' input-' + this.size;
24963         }
24964         
24965         var settings=this;
24966         
24967         ['xs','sm','md','lg'].map(function(size){
24968             if (settings[size]) {
24969                 cfg.cls += ' col-' + size + '-' + settings[size];
24970             }
24971         });
24972         
24973         var inputblock = input;
24974          
24975         if (this.before || this.after) {
24976             
24977             inputblock = {
24978                 cls : 'input-group',
24979                 cn :  [] 
24980             };
24981             
24982             if (this.before) {
24983                 inputblock.cn.push({
24984                     tag :'span',
24985                     cls : 'input-group-addon',
24986                     html : this.before
24987                 });
24988             }
24989             
24990             inputblock.cn.push(input);
24991             
24992             if(this.inputType != 'radio'){
24993                 inputblock.cn.push(hidden);
24994             }
24995             
24996             if (this.after) {
24997                 inputblock.cn.push({
24998                     tag :'span',
24999                     cls : 'input-group-addon',
25000                     html : this.after
25001                 });
25002             }
25003             
25004         }
25005         var boxLabelCfg = false;
25006         
25007         if(this.boxLabel){
25008            
25009             boxLabelCfg = {
25010                 tag: 'label',
25011                 //'for': id, // box label is handled by onclick - so no for...
25012                 cls: 'box-label',
25013                 html: this.boxLabel
25014             };
25015             if(this.tooltip){
25016                 boxLabelCfg.tooltip = this.tooltip;
25017             }
25018              
25019         }
25020         
25021         
25022         if (align ==='left' && this.fieldLabel.length) {
25023 //                Roo.log("left and has label");
25024             cfg.cn = [
25025                 {
25026                     tag: 'label',
25027                     'for' :  id,
25028                     cls : 'control-label',
25029                     html : this.fieldLabel
25030                 },
25031                 {
25032                     cls : "", 
25033                     cn: [
25034                         inputblock
25035                     ]
25036                 }
25037             ];
25038             
25039             if (boxLabelCfg) {
25040                 cfg.cn[1].cn.push(boxLabelCfg);
25041             }
25042             
25043             if(this.labelWidth > 12){
25044                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25045             }
25046             
25047             if(this.labelWidth < 13 && this.labelmd == 0){
25048                 this.labelmd = this.labelWidth;
25049             }
25050             
25051             if(this.labellg > 0){
25052                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25053                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25054             }
25055             
25056             if(this.labelmd > 0){
25057                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25058                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25059             }
25060             
25061             if(this.labelsm > 0){
25062                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25063                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25064             }
25065             
25066             if(this.labelxs > 0){
25067                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25068                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25069             }
25070             
25071         } else if ( this.fieldLabel.length) {
25072 //                Roo.log(" label");
25073                 cfg.cn = [
25074                    
25075                     {
25076                         tag: this.boxLabel ? 'span' : 'label',
25077                         'for': id,
25078                         cls: 'control-label box-input-label',
25079                         //cls : 'input-group-addon',
25080                         html : this.fieldLabel
25081                     },
25082                     
25083                     inputblock
25084                     
25085                 ];
25086                 if (boxLabelCfg) {
25087                     cfg.cn.push(boxLabelCfg);
25088                 }
25089
25090         } else {
25091             
25092 //                Roo.log(" no label && no align");
25093                 cfg.cn = [  inputblock ] ;
25094                 if (boxLabelCfg) {
25095                     cfg.cn.push(boxLabelCfg);
25096                 }
25097
25098                 
25099         }
25100         
25101        
25102         
25103         if(this.inputType != 'radio'){
25104             cfg.cn.push(hidden);
25105         }
25106         
25107         return cfg;
25108         
25109     },
25110     
25111     /**
25112      * return the real input element.
25113      */
25114     inputEl: function ()
25115     {
25116         return this.el.select('input.roo-' + this.inputType,true).first();
25117     },
25118     hiddenEl: function ()
25119     {
25120         return this.el.select('input.roo-hidden-value',true).first();
25121     },
25122     
25123     labelEl: function()
25124     {
25125         return this.el.select('label.control-label',true).first();
25126     },
25127     /* depricated... */
25128     
25129     label: function()
25130     {
25131         return this.labelEl();
25132     },
25133     
25134     boxLabelEl: function()
25135     {
25136         return this.el.select('label.box-label',true).first();
25137     },
25138     
25139     initEvents : function()
25140     {
25141 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25142         
25143         this.inputEl().on('click', this.onClick,  this);
25144         
25145         if (this.boxLabel) { 
25146             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25147         }
25148         
25149         this.startValue = this.getValue();
25150         
25151         if(this.groupId){
25152             Roo.bootstrap.form.CheckBox.register(this);
25153         }
25154     },
25155     
25156     onClick : function(e)
25157     {   
25158         if(this.fireEvent('click', this, e) !== false){
25159             this.setChecked(!this.checked);
25160         }
25161         
25162     },
25163     
25164     setChecked : function(state,suppressEvent)
25165     {
25166         this.startValue = this.getValue();
25167
25168         if(this.inputType == 'radio'){
25169             
25170             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25171                 e.dom.checked = false;
25172             });
25173             
25174             this.inputEl().dom.checked = true;
25175             
25176             this.inputEl().dom.value = this.inputValue;
25177             
25178             if(suppressEvent !== true){
25179                 this.fireEvent('check', this, true);
25180             }
25181             
25182             this.validate();
25183             
25184             return;
25185         }
25186         
25187         this.checked = state;
25188         
25189         this.inputEl().dom.checked = state;
25190         
25191         
25192         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25193         
25194         if(suppressEvent !== true){
25195             this.fireEvent('check', this, state);
25196         }
25197         
25198         this.validate();
25199     },
25200     
25201     getValue : function()
25202     {
25203         if(this.inputType == 'radio'){
25204             return this.getGroupValue();
25205         }
25206         
25207         return this.hiddenEl().dom.value;
25208         
25209     },
25210     
25211     getGroupValue : function()
25212     {
25213         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25214             return '';
25215         }
25216         
25217         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25218     },
25219     
25220     setValue : function(v,suppressEvent)
25221     {
25222         if(this.inputType == 'radio'){
25223             this.setGroupValue(v, suppressEvent);
25224             return;
25225         }
25226         
25227         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25228         
25229         this.validate();
25230     },
25231     
25232     setGroupValue : function(v, suppressEvent)
25233     {
25234         this.startValue = this.getValue();
25235         
25236         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25237             e.dom.checked = false;
25238             
25239             if(e.dom.value == v){
25240                 e.dom.checked = true;
25241             }
25242         });
25243         
25244         if(suppressEvent !== true){
25245             this.fireEvent('check', this, true);
25246         }
25247
25248         this.validate();
25249         
25250         return;
25251     },
25252     
25253     validate : function()
25254     {
25255         if(this.getVisibilityEl().hasClass('hidden')){
25256             return true;
25257         }
25258         
25259         if(
25260                 this.disabled || 
25261                 (this.inputType == 'radio' && this.validateRadio()) ||
25262                 (this.inputType == 'checkbox' && this.validateCheckbox())
25263         ){
25264             this.markValid();
25265             return true;
25266         }
25267         
25268         this.markInvalid();
25269         return false;
25270     },
25271     
25272     validateRadio : function()
25273     {
25274         if(this.getVisibilityEl().hasClass('hidden')){
25275             return true;
25276         }
25277         
25278         if(this.allowBlank){
25279             return true;
25280         }
25281         
25282         var valid = false;
25283         
25284         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25285             if(!e.dom.checked){
25286                 return;
25287             }
25288             
25289             valid = true;
25290             
25291             return false;
25292         });
25293         
25294         return valid;
25295     },
25296     
25297     validateCheckbox : function()
25298     {
25299         if(!this.groupId){
25300             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25301             //return (this.getValue() == this.inputValue) ? true : false;
25302         }
25303         
25304         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25305         
25306         if(!group){
25307             return false;
25308         }
25309         
25310         var r = false;
25311         
25312         for(var i in group){
25313             if(group[i].el.isVisible(true)){
25314                 r = false;
25315                 break;
25316             }
25317             
25318             r = true;
25319         }
25320         
25321         for(var i in group){
25322             if(r){
25323                 break;
25324             }
25325             
25326             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25327         }
25328         
25329         return r;
25330     },
25331     
25332     /**
25333      * Mark this field as valid
25334      */
25335     markValid : function()
25336     {
25337         var _this = this;
25338         
25339         this.fireEvent('valid', this);
25340         
25341         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25342         
25343         if(this.groupId){
25344             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25345         }
25346         
25347         if(label){
25348             label.markValid();
25349         }
25350
25351         if(this.inputType == 'radio'){
25352             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25353                 var fg = e.findParent('.form-group', false, true);
25354                 if (Roo.bootstrap.version == 3) {
25355                     fg.removeClass([_this.invalidClass, _this.validClass]);
25356                     fg.addClass(_this.validClass);
25357                 } else {
25358                     fg.removeClass(['is-valid', 'is-invalid']);
25359                     fg.addClass('is-valid');
25360                 }
25361             });
25362             
25363             return;
25364         }
25365
25366         if(!this.groupId){
25367             var fg = this.el.findParent('.form-group', false, true);
25368             if (Roo.bootstrap.version == 3) {
25369                 fg.removeClass([this.invalidClass, this.validClass]);
25370                 fg.addClass(this.validClass);
25371             } else {
25372                 fg.removeClass(['is-valid', 'is-invalid']);
25373                 fg.addClass('is-valid');
25374             }
25375             return;
25376         }
25377         
25378         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25379         
25380         if(!group){
25381             return;
25382         }
25383         
25384         for(var i in group){
25385             var fg = group[i].el.findParent('.form-group', false, true);
25386             if (Roo.bootstrap.version == 3) {
25387                 fg.removeClass([this.invalidClass, this.validClass]);
25388                 fg.addClass(this.validClass);
25389             } else {
25390                 fg.removeClass(['is-valid', 'is-invalid']);
25391                 fg.addClass('is-valid');
25392             }
25393         }
25394     },
25395     
25396      /**
25397      * Mark this field as invalid
25398      * @param {String} msg The validation message
25399      */
25400     markInvalid : function(msg)
25401     {
25402         if(this.allowBlank){
25403             return;
25404         }
25405         
25406         var _this = this;
25407         
25408         this.fireEvent('invalid', this, msg);
25409         
25410         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25411         
25412         if(this.groupId){
25413             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25414         }
25415         
25416         if(label){
25417             label.markInvalid();
25418         }
25419             
25420         if(this.inputType == 'radio'){
25421             
25422             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25423                 var fg = e.findParent('.form-group', false, true);
25424                 if (Roo.bootstrap.version == 3) {
25425                     fg.removeClass([_this.invalidClass, _this.validClass]);
25426                     fg.addClass(_this.invalidClass);
25427                 } else {
25428                     fg.removeClass(['is-invalid', 'is-valid']);
25429                     fg.addClass('is-invalid');
25430                 }
25431             });
25432             
25433             return;
25434         }
25435         
25436         if(!this.groupId){
25437             var fg = this.el.findParent('.form-group', false, true);
25438             if (Roo.bootstrap.version == 3) {
25439                 fg.removeClass([_this.invalidClass, _this.validClass]);
25440                 fg.addClass(_this.invalidClass);
25441             } else {
25442                 fg.removeClass(['is-invalid', 'is-valid']);
25443                 fg.addClass('is-invalid');
25444             }
25445             return;
25446         }
25447         
25448         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25449         
25450         if(!group){
25451             return;
25452         }
25453         
25454         for(var i in group){
25455             var fg = group[i].el.findParent('.form-group', false, true);
25456             if (Roo.bootstrap.version == 3) {
25457                 fg.removeClass([_this.invalidClass, _this.validClass]);
25458                 fg.addClass(_this.invalidClass);
25459             } else {
25460                 fg.removeClass(['is-invalid', 'is-valid']);
25461                 fg.addClass('is-invalid');
25462             }
25463         }
25464         
25465     },
25466     
25467     clearInvalid : function()
25468     {
25469         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25470         
25471         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25472         
25473         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25474         
25475         if (label && label.iconEl) {
25476             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25477             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25478         }
25479     },
25480     
25481     disable : function()
25482     {
25483         if(this.inputType != 'radio'){
25484             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25485             return;
25486         }
25487         
25488         var _this = this;
25489         
25490         if(this.rendered){
25491             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25492                 _this.getActionEl().addClass(this.disabledClass);
25493                 e.dom.disabled = true;
25494             });
25495         }
25496         
25497         this.disabled = true;
25498         this.fireEvent("disable", this);
25499         return this;
25500     },
25501
25502     enable : function()
25503     {
25504         if(this.inputType != 'radio'){
25505             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25506             return;
25507         }
25508         
25509         var _this = this;
25510         
25511         if(this.rendered){
25512             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25513                 _this.getActionEl().removeClass(this.disabledClass);
25514                 e.dom.disabled = false;
25515             });
25516         }
25517         
25518         this.disabled = false;
25519         this.fireEvent("enable", this);
25520         return this;
25521     },
25522     
25523     setBoxLabel : function(v)
25524     {
25525         this.boxLabel = v;
25526         
25527         if(this.rendered){
25528             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25529         }
25530     }
25531
25532 });
25533
25534 Roo.apply(Roo.bootstrap.form.CheckBox, {
25535     
25536     groups: {},
25537     
25538      /**
25539     * register a CheckBox Group
25540     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25541     */
25542     register : function(checkbox)
25543     {
25544         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25545             this.groups[checkbox.groupId] = {};
25546         }
25547         
25548         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25549             return;
25550         }
25551         
25552         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25553         
25554     },
25555     /**
25556     * fetch a CheckBox Group based on the group ID
25557     * @param {string} the group ID
25558     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25559     */
25560     get: function(groupId) {
25561         if (typeof(this.groups[groupId]) == 'undefined') {
25562             return false;
25563         }
25564         
25565         return this.groups[groupId] ;
25566     }
25567     
25568     
25569 });
25570 /*
25571  * - LGPL
25572  *
25573  * RadioItem
25574  * 
25575  */
25576
25577 /**
25578  * @class Roo.bootstrap.form.Radio
25579  * @extends Roo.bootstrap.Component
25580  * Bootstrap Radio class
25581  * @cfg {String} boxLabel - the label associated
25582  * @cfg {String} value - the value of radio
25583  * 
25584  * @constructor
25585  * Create a new Radio
25586  * @param {Object} config The config object
25587  */
25588 Roo.bootstrap.form.Radio = function(config){
25589     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25590     
25591 };
25592
25593 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25594     
25595     boxLabel : '',
25596     
25597     value : '',
25598     
25599     getAutoCreate : function()
25600     {
25601         var cfg = {
25602             tag : 'div',
25603             cls : 'form-group radio',
25604             cn : [
25605                 {
25606                     tag : 'label',
25607                     cls : 'box-label',
25608                     html : this.boxLabel
25609                 }
25610             ]
25611         };
25612         
25613         return cfg;
25614     },
25615     
25616     initEvents : function() 
25617     {
25618         this.parent().register(this);
25619         
25620         this.el.on('click', this.onClick, this);
25621         
25622     },
25623     
25624     onClick : function(e)
25625     {
25626         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25627             this.setChecked(true);
25628         }
25629     },
25630     
25631     setChecked : function(state, suppressEvent)
25632     {
25633         this.parent().setValue(this.value, suppressEvent);
25634         
25635     },
25636     
25637     setBoxLabel : function(v)
25638     {
25639         this.boxLabel = v;
25640         
25641         if(this.rendered){
25642             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25643         }
25644     }
25645     
25646 });
25647  
25648
25649  /*
25650  * - LGPL
25651  *
25652  * Input
25653  * 
25654  */
25655
25656 /**
25657  * @class Roo.bootstrap.form.SecurePass
25658  * @extends Roo.bootstrap.form.Input
25659  * Bootstrap SecurePass class
25660  *
25661  * 
25662  * @constructor
25663  * Create a new SecurePass
25664  * @param {Object} config The config object
25665  */
25666  
25667 Roo.bootstrap.form.SecurePass = function (config) {
25668     // these go here, so the translation tool can replace them..
25669     this.errors = {
25670         PwdEmpty: "Please type a password, and then retype it to confirm.",
25671         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25672         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25673         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25674         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25675         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25676         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25677         TooWeak: "Your password is Too Weak."
25678     },
25679     this.meterLabel = "Password strength:";
25680     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25681     this.meterClass = [
25682         "roo-password-meter-tooweak", 
25683         "roo-password-meter-weak", 
25684         "roo-password-meter-medium", 
25685         "roo-password-meter-strong", 
25686         "roo-password-meter-grey"
25687     ];
25688     
25689     this.errors = {};
25690     
25691     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25692 }
25693
25694 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25695     /**
25696      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25697      * {
25698      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25699      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25700      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25701      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25702      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25703      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25704      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25705      * })
25706      */
25707     // private
25708     
25709     meterWidth: 300,
25710     errorMsg :'',    
25711     errors: false,
25712     imageRoot: '/',
25713     /**
25714      * @cfg {String/Object} Label for the strength meter (defaults to
25715      * 'Password strength:')
25716      */
25717     // private
25718     meterLabel: '',
25719     /**
25720      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25721      * ['Weak', 'Medium', 'Strong'])
25722      */
25723     // private    
25724     pwdStrengths: false,    
25725     // private
25726     strength: 0,
25727     // private
25728     _lastPwd: null,
25729     // private
25730     kCapitalLetter: 0,
25731     kSmallLetter: 1,
25732     kDigit: 2,
25733     kPunctuation: 3,
25734     
25735     insecure: false,
25736     // private
25737     initEvents: function ()
25738     {
25739         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25740
25741         if (this.el.is('input[type=password]') && Roo.isSafari) {
25742             this.el.on('keydown', this.SafariOnKeyDown, this);
25743         }
25744
25745         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25746     },
25747     // private
25748     onRender: function (ct, position)
25749     {
25750         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25751         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25752         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25753
25754         this.trigger.createChild({
25755                    cn: [
25756                     {
25757                     //id: 'PwdMeter',
25758                     tag: 'div',
25759                     cls: 'roo-password-meter-grey col-xs-12',
25760                     style: {
25761                         //width: 0,
25762                         //width: this.meterWidth + 'px'                                                
25763                         }
25764                     },
25765                     {                            
25766                          cls: 'roo-password-meter-text'                          
25767                     }
25768                 ]            
25769         });
25770
25771          
25772         if (this.hideTrigger) {
25773             this.trigger.setDisplayed(false);
25774         }
25775         this.setSize(this.width || '', this.height || '');
25776     },
25777     // private
25778     onDestroy: function ()
25779     {
25780         if (this.trigger) {
25781             this.trigger.removeAllListeners();
25782             this.trigger.remove();
25783         }
25784         if (this.wrap) {
25785             this.wrap.remove();
25786         }
25787         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25788     },
25789     // private
25790     checkStrength: function ()
25791     {
25792         var pwd = this.inputEl().getValue();
25793         if (pwd == this._lastPwd) {
25794             return;
25795         }
25796
25797         var strength;
25798         if (this.ClientSideStrongPassword(pwd)) {
25799             strength = 3;
25800         } else if (this.ClientSideMediumPassword(pwd)) {
25801             strength = 2;
25802         } else if (this.ClientSideWeakPassword(pwd)) {
25803             strength = 1;
25804         } else {
25805             strength = 0;
25806         }
25807         
25808         Roo.log('strength1: ' + strength);
25809         
25810         //var pm = this.trigger.child('div/div/div').dom;
25811         var pm = this.trigger.child('div/div');
25812         pm.removeClass(this.meterClass);
25813         pm.addClass(this.meterClass[strength]);
25814                 
25815         
25816         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25817                 
25818         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25819         
25820         this._lastPwd = pwd;
25821     },
25822     reset: function ()
25823     {
25824         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25825         
25826         this._lastPwd = '';
25827         
25828         var pm = this.trigger.child('div/div');
25829         pm.removeClass(this.meterClass);
25830         pm.addClass('roo-password-meter-grey');        
25831         
25832         
25833         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25834         
25835         pt.innerHTML = '';
25836         this.inputEl().dom.type='password';
25837     },
25838     // private
25839     validateValue: function (value)
25840     {
25841         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25842             return false;
25843         }
25844         if (value.length == 0) {
25845             if (this.allowBlank) {
25846                 this.clearInvalid();
25847                 return true;
25848             }
25849
25850             this.markInvalid(this.errors.PwdEmpty);
25851             this.errorMsg = this.errors.PwdEmpty;
25852             return false;
25853         }
25854         
25855         if(this.insecure){
25856             return true;
25857         }
25858         
25859         if (!value.match(/[\x21-\x7e]+/)) {
25860             this.markInvalid(this.errors.PwdBadChar);
25861             this.errorMsg = this.errors.PwdBadChar;
25862             return false;
25863         }
25864         if (value.length < 6) {
25865             this.markInvalid(this.errors.PwdShort);
25866             this.errorMsg = this.errors.PwdShort;
25867             return false;
25868         }
25869         if (value.length > 16) {
25870             this.markInvalid(this.errors.PwdLong);
25871             this.errorMsg = this.errors.PwdLong;
25872             return false;
25873         }
25874         var strength;
25875         if (this.ClientSideStrongPassword(value)) {
25876             strength = 3;
25877         } else if (this.ClientSideMediumPassword(value)) {
25878             strength = 2;
25879         } else if (this.ClientSideWeakPassword(value)) {
25880             strength = 1;
25881         } else {
25882             strength = 0;
25883         }
25884
25885         
25886         if (strength < 2) {
25887             //this.markInvalid(this.errors.TooWeak);
25888             this.errorMsg = this.errors.TooWeak;
25889             //return false;
25890         }
25891         
25892         
25893         console.log('strength2: ' + strength);
25894         
25895         //var pm = this.trigger.child('div/div/div').dom;
25896         
25897         var pm = this.trigger.child('div/div');
25898         pm.removeClass(this.meterClass);
25899         pm.addClass(this.meterClass[strength]);
25900                 
25901         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25902                 
25903         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25904         
25905         this.errorMsg = ''; 
25906         return true;
25907     },
25908     // private
25909     CharacterSetChecks: function (type)
25910     {
25911         this.type = type;
25912         this.fResult = false;
25913     },
25914     // private
25915     isctype: function (character, type)
25916     {
25917         switch (type) {  
25918             case this.kCapitalLetter:
25919                 if (character >= 'A' && character <= 'Z') {
25920                     return true;
25921                 }
25922                 break;
25923             
25924             case this.kSmallLetter:
25925                 if (character >= 'a' && character <= 'z') {
25926                     return true;
25927                 }
25928                 break;
25929             
25930             case this.kDigit:
25931                 if (character >= '0' && character <= '9') {
25932                     return true;
25933                 }
25934                 break;
25935             
25936             case this.kPunctuation:
25937                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25938                     return true;
25939                 }
25940                 break;
25941             
25942             default:
25943                 return false;
25944         }
25945
25946     },
25947     // private
25948     IsLongEnough: function (pwd, size)
25949     {
25950         return !(pwd == null || isNaN(size) || pwd.length < size);
25951     },
25952     // private
25953     SpansEnoughCharacterSets: function (word, nb)
25954     {
25955         if (!this.IsLongEnough(word, nb))
25956         {
25957             return false;
25958         }
25959
25960         var characterSetChecks = new Array(
25961             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25962             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25963         );
25964         
25965         for (var index = 0; index < word.length; ++index) {
25966             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25967                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25968                     characterSetChecks[nCharSet].fResult = true;
25969                     break;
25970                 }
25971             }
25972         }
25973
25974         var nCharSets = 0;
25975         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25976             if (characterSetChecks[nCharSet].fResult) {
25977                 ++nCharSets;
25978             }
25979         }
25980
25981         if (nCharSets < nb) {
25982             return false;
25983         }
25984         return true;
25985     },
25986     // private
25987     ClientSideStrongPassword: function (pwd)
25988     {
25989         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25990     },
25991     // private
25992     ClientSideMediumPassword: function (pwd)
25993     {
25994         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25995     },
25996     // private
25997     ClientSideWeakPassword: function (pwd)
25998     {
25999         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26000     }
26001           
26002 });
26003 Roo.htmleditor = {};
26004  
26005 /**
26006  * @class Roo.htmleditor.Filter
26007  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26008  * @cfg {DomElement} node The node to iterate and filter
26009  * @cfg {boolean|String|Array} tag Tags to replace 
26010  * @constructor
26011  * Create a new Filter.
26012  * @param {Object} config Configuration options
26013  */
26014
26015
26016
26017 Roo.htmleditor.Filter = function(cfg) {
26018     Roo.apply(this.cfg);
26019     // this does not actually call walk as it's really just a abstract class
26020 }
26021
26022
26023 Roo.htmleditor.Filter.prototype = {
26024     
26025     node: false,
26026     
26027     tag: false,
26028
26029     // overrride to do replace comments.
26030     replaceComment : false,
26031     
26032     // overrride to do replace or do stuff with tags..
26033     replaceTag : false,
26034     
26035     walk : function(dom)
26036     {
26037         Roo.each( Array.from(dom.childNodes), function( e ) {
26038             switch(true) {
26039                 
26040                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26041                     this.replaceComment(e);
26042                     return;
26043                 
26044                 case e.nodeType != 1: //not a node.
26045                     return;
26046                 
26047                 case this.tag === true: // everything
26048                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26049                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26050                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26051                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26052                     if (this.replaceTag && false === this.replaceTag(e)) {
26053                         return;
26054                     }
26055                     if (e.hasChildNodes()) {
26056                         this.walk(e);
26057                     }
26058                     return;
26059                 
26060                 default:    // tags .. that do not match.
26061                     if (e.hasChildNodes()) {
26062                         this.walk(e);
26063                     }
26064             }
26065             
26066         }, this);
26067         
26068     },
26069     
26070     
26071     removeNodeKeepChildren : function( node)
26072     {
26073     
26074         ar = Array.from(node.childNodes);
26075         for (var i = 0; i < ar.length; i++) {
26076          
26077             node.removeChild(ar[i]);
26078             // what if we need to walk these???
26079             node.parentNode.insertBefore(ar[i], node);
26080            
26081         }
26082         node.parentNode.removeChild(node);
26083     }
26084 }; 
26085
26086 /**
26087  * @class Roo.htmleditor.FilterAttributes
26088  * clean attributes and  styles including http:// etc.. in attribute
26089  * @constructor
26090 * Run a new Attribute Filter
26091 * @param {Object} config Configuration options
26092  */
26093 Roo.htmleditor.FilterAttributes = function(cfg)
26094 {
26095     Roo.apply(this, cfg);
26096     this.attrib_black = this.attrib_black || [];
26097     this.attrib_white = this.attrib_white || [];
26098
26099     this.attrib_clean = this.attrib_clean || [];
26100     this.style_white = this.style_white || [];
26101     this.style_black = this.style_black || [];
26102     this.walk(cfg.node);
26103 }
26104
26105 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26106 {
26107     tag: true, // all tags
26108     
26109     attrib_black : false, // array
26110     attrib_clean : false,
26111     attrib_white : false,
26112
26113     style_white : false,
26114     style_black : false,
26115      
26116      
26117     replaceTag : function(node)
26118     {
26119         if (!node.attributes || !node.attributes.length) {
26120             return true;
26121         }
26122         
26123         for (var i = node.attributes.length-1; i > -1 ; i--) {
26124             var a = node.attributes[i];
26125             //console.log(a);
26126             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26127                 node.removeAttribute(a.name);
26128                 continue;
26129             }
26130             
26131             
26132             
26133             if (a.name.toLowerCase().substr(0,2)=='on')  {
26134                 node.removeAttribute(a.name);
26135                 continue;
26136             }
26137             
26138             
26139             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26140                 node.removeAttribute(a.name);
26141                 continue;
26142             }
26143             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26144                 this.cleanAttr(node,a.name,a.value); // fixme..
26145                 continue;
26146             }
26147             if (a.name == 'style') {
26148                 this.cleanStyle(node,a.name,a.value);
26149                 continue;
26150             }
26151             /// clean up MS crap..
26152             // tecnically this should be a list of valid class'es..
26153             
26154             
26155             if (a.name == 'class') {
26156                 if (a.value.match(/^Mso/)) {
26157                     node.removeAttribute('class');
26158                 }
26159                 
26160                 if (a.value.match(/^body$/)) {
26161                     node.removeAttribute('class');
26162                 }
26163                 continue;
26164             }
26165             
26166             
26167             // style cleanup!?
26168             // class cleanup?
26169             
26170         }
26171         return true; // clean children
26172     },
26173         
26174     cleanAttr: function(node, n,v)
26175     {
26176         
26177         if (v.match(/^\./) || v.match(/^\//)) {
26178             return;
26179         }
26180         if (v.match(/^(http|https):\/\//)
26181             || v.match(/^mailto:/) 
26182             || v.match(/^ftp:/)
26183             || v.match(/^data:/)
26184             ) {
26185             return;
26186         }
26187         if (v.match(/^#/)) {
26188             return;
26189         }
26190         if (v.match(/^\{/)) { // allow template editing.
26191             return;
26192         }
26193 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26194         node.removeAttribute(n);
26195         
26196     },
26197     cleanStyle : function(node,  n,v)
26198     {
26199         if (v.match(/expression/)) { //XSS?? should we even bother..
26200             node.removeAttribute(n);
26201             return;
26202         }
26203         
26204         var parts = v.split(/;/);
26205         var clean = [];
26206         
26207         Roo.each(parts, function(p) {
26208             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26209             if (!p.length) {
26210                 return true;
26211             }
26212             var l = p.split(':').shift().replace(/\s+/g,'');
26213             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26214             
26215             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26216                 return true;
26217             }
26218             //Roo.log()
26219             // only allow 'c whitelisted system attributes'
26220             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26221                 return true;
26222             }
26223             
26224             
26225             clean.push(p);
26226             return true;
26227         },this);
26228         if (clean.length) { 
26229             node.setAttribute(n, clean.join(';'));
26230         } else {
26231             node.removeAttribute(n);
26232         }
26233         
26234     }
26235         
26236         
26237         
26238     
26239 });/**
26240  * @class Roo.htmleditor.FilterBlack
26241  * remove blacklisted elements.
26242  * @constructor
26243  * Run a new Blacklisted Filter
26244  * @param {Object} config Configuration options
26245  */
26246
26247 Roo.htmleditor.FilterBlack = function(cfg)
26248 {
26249     Roo.apply(this, cfg);
26250     this.walk(cfg.node);
26251 }
26252
26253 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26254 {
26255     tag : true, // all elements.
26256    
26257     replaceTag : function(n)
26258     {
26259         n.parentNode.removeChild(n);
26260     }
26261 });
26262 /**
26263  * @class Roo.htmleditor.FilterComment
26264  * remove comments.
26265  * @constructor
26266 * Run a new Comments Filter
26267 * @param {Object} config Configuration options
26268  */
26269 Roo.htmleditor.FilterComment = function(cfg)
26270 {
26271     this.walk(cfg.node);
26272 }
26273
26274 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26275 {
26276   
26277     replaceComment : function(n)
26278     {
26279         n.parentNode.removeChild(n);
26280     }
26281 });/**
26282  * @class Roo.htmleditor.FilterKeepChildren
26283  * remove tags but keep children
26284  * @constructor
26285  * Run a new Keep Children Filter
26286  * @param {Object} config Configuration options
26287  */
26288
26289 Roo.htmleditor.FilterKeepChildren = function(cfg)
26290 {
26291     Roo.apply(this, cfg);
26292     if (this.tag === false) {
26293         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26294     }
26295     // hacky?
26296     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26297         this.cleanNamespace = true;
26298     }
26299         
26300     this.walk(cfg.node);
26301 }
26302
26303 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26304 {
26305     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26306   
26307     replaceTag : function(node)
26308     {
26309         // walk children...
26310         //Roo.log(node.tagName);
26311         var ar = Array.from(node.childNodes);
26312         //remove first..
26313         
26314         for (var i = 0; i < ar.length; i++) {
26315             var e = ar[i];
26316             if (e.nodeType == 1) {
26317                 if (
26318                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26319                     || // array and it matches
26320                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26321                     ||
26322                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26323                     ||
26324                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26325                 ) {
26326                     this.replaceTag(ar[i]); // child is blacklisted as well...
26327                     continue;
26328                 }
26329             }
26330         }  
26331         ar = Array.from(node.childNodes);
26332         for (var i = 0; i < ar.length; i++) {
26333          
26334             node.removeChild(ar[i]);
26335             // what if we need to walk these???
26336             node.parentNode.insertBefore(ar[i], node);
26337             if (this.tag !== false) {
26338                 this.walk(ar[i]);
26339                 
26340             }
26341         }
26342         //Roo.log("REMOVE:" + node.tagName);
26343         node.parentNode.removeChild(node);
26344         return false; // don't walk children
26345         
26346         
26347     }
26348 });/**
26349  * @class Roo.htmleditor.FilterParagraph
26350  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26351  * like on 'push' to remove the <p> tags and replace them with line breaks.
26352  * @constructor
26353  * Run a new Paragraph Filter
26354  * @param {Object} config Configuration options
26355  */
26356
26357 Roo.htmleditor.FilterParagraph = function(cfg)
26358 {
26359     // no need to apply config.
26360     this.walk(cfg.node);
26361 }
26362
26363 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26364 {
26365     
26366      
26367     tag : 'P',
26368     
26369      
26370     replaceTag : function(node)
26371     {
26372         
26373         if (node.childNodes.length == 1 &&
26374             node.childNodes[0].nodeType == 3 &&
26375             node.childNodes[0].textContent.trim().length < 1
26376             ) {
26377             // remove and replace with '<BR>';
26378             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26379             return false; // no need to walk..
26380         }
26381         var ar = Array.from(node.childNodes);
26382         for (var i = 0; i < ar.length; i++) {
26383             node.removeChild(ar[i]);
26384             // what if we need to walk these???
26385             node.parentNode.insertBefore(ar[i], node);
26386         }
26387         // now what about this?
26388         // <p> &nbsp; </p>
26389         
26390         // double BR.
26391         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26392         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26393         node.parentNode.removeChild(node);
26394         
26395         return false;
26396
26397     }
26398     
26399 });/**
26400  * @class Roo.htmleditor.FilterSpan
26401  * filter span's with no attributes out..
26402  * @constructor
26403  * Run a new Span Filter
26404  * @param {Object} config Configuration options
26405  */
26406
26407 Roo.htmleditor.FilterSpan = function(cfg)
26408 {
26409     // no need to apply config.
26410     this.walk(cfg.node);
26411 }
26412
26413 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26414 {
26415      
26416     tag : 'SPAN',
26417      
26418  
26419     replaceTag : function(node)
26420     {
26421         if (node.attributes && node.attributes.length > 0) {
26422             return true; // walk if there are any.
26423         }
26424         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26425         return false;
26426      
26427     }
26428     
26429 });/**
26430  * @class Roo.htmleditor.FilterTableWidth
26431   try and remove table width data - as that frequently messes up other stuff.
26432  * 
26433  *      was cleanTableWidths.
26434  *
26435  * Quite often pasting from word etc.. results in tables with column and widths.
26436  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26437  *
26438  * @constructor
26439  * Run a new Table Filter
26440  * @param {Object} config Configuration options
26441  */
26442
26443 Roo.htmleditor.FilterTableWidth = function(cfg)
26444 {
26445     // no need to apply config.
26446     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26447     this.walk(cfg.node);
26448 }
26449
26450 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26451 {
26452      
26453      
26454     
26455     replaceTag: function(node) {
26456         
26457         
26458       
26459         if (node.hasAttribute('width')) {
26460             node.removeAttribute('width');
26461         }
26462         
26463          
26464         if (node.hasAttribute("style")) {
26465             // pretty basic...
26466             
26467             var styles = node.getAttribute("style").split(";");
26468             var nstyle = [];
26469             Roo.each(styles, function(s) {
26470                 if (!s.match(/:/)) {
26471                     return;
26472                 }
26473                 var kv = s.split(":");
26474                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26475                     return;
26476                 }
26477                 // what ever is left... we allow.
26478                 nstyle.push(s);
26479             });
26480             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26481             if (!nstyle.length) {
26482                 node.removeAttribute('style');
26483             }
26484         }
26485         
26486         return true; // continue doing children..
26487     }
26488 });/**
26489  * @class Roo.htmleditor.FilterWord
26490  * try and clean up all the mess that Word generates.
26491  * 
26492  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26493  
26494  * @constructor
26495  * Run a new Span Filter
26496  * @param {Object} config Configuration options
26497  */
26498
26499 Roo.htmleditor.FilterWord = function(cfg)
26500 {
26501     // no need to apply config.
26502     this.replaceDocBullets(cfg.node);
26503     
26504     this.replaceAname(cfg.node);
26505     // this is disabled as the removal is done by other filters;
26506    // this.walk(cfg.node);
26507     
26508     
26509 }
26510
26511 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26512 {
26513     tag: true,
26514      
26515     
26516     /**
26517      * Clean up MS wordisms...
26518      */
26519     replaceTag : function(node)
26520     {
26521          
26522         // no idea what this does - span with text, replaceds with just text.
26523         if(
26524                 node.nodeName == 'SPAN' &&
26525                 !node.hasAttributes() &&
26526                 node.childNodes.length == 1 &&
26527                 node.firstChild.nodeName == "#text"  
26528         ) {
26529             var textNode = node.firstChild;
26530             node.removeChild(textNode);
26531             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26532                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26533             }
26534             node.parentNode.insertBefore(textNode, node);
26535             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26536                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26537             }
26538             
26539             node.parentNode.removeChild(node);
26540             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26541         }
26542         
26543    
26544         
26545         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26546             node.parentNode.removeChild(node);
26547             return false; // dont do chidlren
26548         }
26549         //Roo.log(node.tagName);
26550         // remove - but keep children..
26551         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26552             //Roo.log('-- removed');
26553             while (node.childNodes.length) {
26554                 var cn = node.childNodes[0];
26555                 node.removeChild(cn);
26556                 node.parentNode.insertBefore(cn, node);
26557                 // move node to parent - and clean it..
26558                 if (cn.nodeType == 1) {
26559                     this.replaceTag(cn);
26560                 }
26561                 
26562             }
26563             node.parentNode.removeChild(node);
26564             /// no need to iterate chidlren = it's got none..
26565             //this.iterateChildren(node, this.cleanWord);
26566             return false; // no need to iterate children.
26567         }
26568         // clean styles
26569         if (node.className.length) {
26570             
26571             var cn = node.className.split(/\W+/);
26572             var cna = [];
26573             Roo.each(cn, function(cls) {
26574                 if (cls.match(/Mso[a-zA-Z]+/)) {
26575                     return;
26576                 }
26577                 cna.push(cls);
26578             });
26579             node.className = cna.length ? cna.join(' ') : '';
26580             if (!cna.length) {
26581                 node.removeAttribute("class");
26582             }
26583         }
26584         
26585         if (node.hasAttribute("lang")) {
26586             node.removeAttribute("lang");
26587         }
26588         
26589         if (node.hasAttribute("style")) {
26590             
26591             var styles = node.getAttribute("style").split(";");
26592             var nstyle = [];
26593             Roo.each(styles, function(s) {
26594                 if (!s.match(/:/)) {
26595                     return;
26596                 }
26597                 var kv = s.split(":");
26598                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26599                     return;
26600                 }
26601                 // what ever is left... we allow.
26602                 nstyle.push(s);
26603             });
26604             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26605             if (!nstyle.length) {
26606                 node.removeAttribute('style');
26607             }
26608         }
26609         return true; // do children
26610         
26611         
26612         
26613     },
26614     
26615     styleToObject: function(node)
26616     {
26617         var styles = (node.getAttribute("style") || '').split(";");
26618         var ret = {};
26619         Roo.each(styles, function(s) {
26620             if (!s.match(/:/)) {
26621                 return;
26622             }
26623             var kv = s.split(":");
26624              
26625             // what ever is left... we allow.
26626             ret[kv[0].trim()] = kv[1];
26627         });
26628         return ret;
26629     },
26630     
26631     
26632     replaceAname : function (doc)
26633     {
26634         // replace all the a/name without..
26635         var aa = Array.from(doc.getElementsByTagName('a'));
26636         for (var i = 0; i  < aa.length; i++) {
26637             var a = aa[i];
26638             if (a.hasAttribute("name")) {
26639                 a.removeAttribute("name");
26640             }
26641             if (a.hasAttribute("href")) {
26642                 continue;
26643             }
26644             // reparent children.
26645             this.removeNodeKeepChildren(a);
26646             
26647         }
26648         
26649         
26650         
26651     },
26652
26653     
26654     
26655     replaceDocBullets : function(doc)
26656     {
26657         // this is a bit odd - but it appears some indents use ql-indent-1
26658          //Roo.log(doc.innerHTML);
26659         
26660         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26661         for( var i = 0; i < listpara.length; i ++) {
26662             listpara[i].className = "MsoListParagraph";
26663         }
26664         
26665         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26666         for( var i = 0; i < listpara.length; i ++) {
26667             listpara[i].className = "MsoListParagraph";
26668         }
26669         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26670         for( var i = 0; i < listpara.length; i ++) {
26671             listpara[i].className = "MsoListParagraph";
26672         }
26673         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26674         for( var i = 0; i < listpara.length; i ++) {
26675             listpara[i].className = "MsoListParagraph";
26676         }
26677         
26678         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26679         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26680         for( var i = 0; i < htwo.length; i ++) {
26681             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26682                 htwo[i].className = "MsoListParagraph";
26683             }
26684         }
26685         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26686         for( var i = 0; i < listpara.length; i ++) {
26687             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26688                 listpara[i].className = "MsoListParagraph";
26689             } else {
26690                 listpara[i].className = "MsoNormalx";
26691             }
26692         }
26693        
26694         listpara = doc.getElementsByClassName('MsoListParagraph');
26695         // Roo.log(doc.innerHTML);
26696         
26697         
26698         
26699         while(listpara.length) {
26700             
26701             this.replaceDocBullet(listpara.item(0));
26702         }
26703       
26704     },
26705     
26706      
26707     
26708     replaceDocBullet : function(p)
26709     {
26710         // gather all the siblings.
26711         var ns = p,
26712             parent = p.parentNode,
26713             doc = parent.ownerDocument,
26714             items = [];
26715             
26716         var listtype = 'ul';   
26717         while (ns) {
26718             if (ns.nodeType != 1) {
26719                 ns = ns.nextSibling;
26720                 continue;
26721             }
26722             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26723                 break;
26724             }
26725             var spans = ns.getElementsByTagName('span');
26726             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26727                 items.push(ns);
26728                 ns = ns.nextSibling;
26729                 has_list = true;
26730                 if (spans.length && spans[0].hasAttribute('style')) {
26731                     var  style = this.styleToObject(spans[0]);
26732                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26733                         listtype = 'ol';
26734                     }
26735                 }
26736                 
26737                 continue;
26738             }
26739             var spans = ns.getElementsByTagName('span');
26740             if (!spans.length) {
26741                 break;
26742             }
26743             var has_list  = false;
26744             for(var i = 0; i < spans.length; i++) {
26745                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26746                     has_list = true;
26747                     break;
26748                 }
26749             }
26750             if (!has_list) {
26751                 break;
26752             }
26753             items.push(ns);
26754             ns = ns.nextSibling;
26755             
26756             
26757         }
26758         if (!items.length) {
26759             ns.className = "";
26760             return;
26761         }
26762         
26763         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26764         parent.insertBefore(ul, p);
26765         var lvl = 0;
26766         var stack = [ ul ];
26767         var last_li = false;
26768         
26769         var margin_to_depth = {};
26770         max_margins = -1;
26771         
26772         items.forEach(function(n, ipos) {
26773             //Roo.log("got innertHMLT=" + n.innerHTML);
26774             
26775             var spans = n.getElementsByTagName('span');
26776             if (!spans.length) {
26777                 //Roo.log("No spans found");
26778                  
26779                 parent.removeChild(n);
26780                 
26781                 
26782                 return; // skip it...
26783             }
26784            
26785                 
26786             var num = 1;
26787             var style = {};
26788             for(var i = 0; i < spans.length; i++) {
26789             
26790                 style = this.styleToObject(spans[i]);
26791                 if (typeof(style['mso-list']) == 'undefined') {
26792                     continue;
26793                 }
26794                 if (listtype == 'ol') {
26795                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26796                 }
26797                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26798                 break;
26799             }
26800             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26801             style = this.styleToObject(n); // mo-list is from the parent node.
26802             if (typeof(style['mso-list']) == 'undefined') {
26803                 //Roo.log("parent is missing level");
26804                   
26805                 parent.removeChild(n);
26806                  
26807                 return;
26808             }
26809             
26810             var margin = style['margin-left'];
26811             if (typeof(margin_to_depth[margin]) == 'undefined') {
26812                 max_margins++;
26813                 margin_to_depth[margin] = max_margins;
26814             }
26815             nlvl = margin_to_depth[margin] ;
26816              
26817             if (nlvl > lvl) {
26818                 //new indent
26819                 var nul = doc.createElement(listtype); // what about number lists...
26820                 if (!last_li) {
26821                     last_li = doc.createElement('li');
26822                     stack[lvl].appendChild(last_li);
26823                 }
26824                 last_li.appendChild(nul);
26825                 stack[nlvl] = nul;
26826                 
26827             }
26828             lvl = nlvl;
26829             
26830             // not starting at 1..
26831             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26832                 stack[nlvl].setAttribute("start", num);
26833             }
26834             
26835             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26836             last_li = nli;
26837             nli.innerHTML = n.innerHTML;
26838             //Roo.log("innerHTML = " + n.innerHTML);
26839             parent.removeChild(n);
26840             
26841              
26842              
26843             
26844         },this);
26845         
26846         
26847         
26848         
26849     }
26850     
26851     
26852     
26853 });
26854 /**
26855  * @class Roo.htmleditor.FilterStyleToTag
26856  * part of the word stuff... - certain 'styles' should be converted to tags.
26857  * eg.
26858  *   font-weight: bold -> bold
26859  *   ?? super / subscrit etc..
26860  * 
26861  * @constructor
26862 * Run a new style to tag filter.
26863 * @param {Object} config Configuration options
26864  */
26865 Roo.htmleditor.FilterStyleToTag = function(cfg)
26866 {
26867     
26868     this.tags = {
26869         B  : [ 'fontWeight' , 'bold'],
26870         I :  [ 'fontStyle' , 'italic'],
26871         //pre :  [ 'font-style' , 'italic'],
26872         // h1.. h6 ?? font-size?
26873         SUP : [ 'verticalAlign' , 'super' ],
26874         SUB : [ 'verticalAlign' , 'sub' ]
26875         
26876         
26877     };
26878     
26879     Roo.apply(this, cfg);
26880      
26881     
26882     this.walk(cfg.node);
26883     
26884     
26885     
26886 }
26887
26888
26889 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26890 {
26891     tag: true, // all tags
26892     
26893     tags : false,
26894     
26895     
26896     replaceTag : function(node)
26897     {
26898         
26899         
26900         if (node.getAttribute("style") === null) {
26901             return true;
26902         }
26903         var inject = [];
26904         for (var k in this.tags) {
26905             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26906                 inject.push(k);
26907                 node.style.removeProperty(this.tags[k][0]);
26908             }
26909         }
26910         if (!inject.length) {
26911             return true; 
26912         }
26913         var cn = Array.from(node.childNodes);
26914         var nn = node;
26915         Roo.each(inject, function(t) {
26916             var nc = node.ownerDocument.createElement(t);
26917             nn.appendChild(nc);
26918             nn = nc;
26919         });
26920         for(var i = 0;i < cn.length;cn++) {
26921             node.removeChild(cn[i]);
26922             nn.appendChild(cn[i]);
26923         }
26924         return true /// iterate thru
26925     }
26926     
26927 })/**
26928  * @class Roo.htmleditor.FilterLongBr
26929  * BR/BR/BR - keep a maximum of 2...
26930  * @constructor
26931  * Run a new Long BR Filter
26932  * @param {Object} config Configuration options
26933  */
26934
26935 Roo.htmleditor.FilterLongBr = function(cfg)
26936 {
26937     // no need to apply config.
26938     this.walk(cfg.node);
26939 }
26940
26941 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26942 {
26943     
26944      
26945     tag : 'BR',
26946     
26947      
26948     replaceTag : function(node)
26949     {
26950         
26951         var ps = node.nextSibling;
26952         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26953             ps = ps.nextSibling;
26954         }
26955         
26956         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26957             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26958             return false;
26959         }
26960         
26961         if (!ps || ps.nodeType != 1) {
26962             return false;
26963         }
26964         
26965         if (!ps || ps.tagName != 'BR') {
26966            
26967             return false;
26968         }
26969         
26970         
26971         
26972         
26973         
26974         if (!node.previousSibling) {
26975             return false;
26976         }
26977         var ps = node.previousSibling;
26978         
26979         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26980             ps = ps.previousSibling;
26981         }
26982         if (!ps || ps.nodeType != 1) {
26983             return false;
26984         }
26985         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26986         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26987             return false;
26988         }
26989         
26990         node.parentNode.removeChild(node); // remove me...
26991         
26992         return false; // no need to do children
26993
26994     }
26995     
26996 }); 
26997
26998 /**
26999  * @class Roo.htmleditor.FilterBlock
27000  * removes id / data-block and contenteditable that are associated with blocks
27001  * usage should be done on a cloned copy of the dom
27002  * @constructor
27003 * Run a new Attribute Filter { node : xxxx }}
27004 * @param {Object} config Configuration options
27005  */
27006 Roo.htmleditor.FilterBlock = function(cfg)
27007 {
27008     Roo.apply(this, cfg);
27009     var qa = cfg.node.querySelectorAll;
27010     this.removeAttributes('data-block');
27011     this.removeAttributes('contenteditable');
27012     this.removeAttributes('id');
27013     
27014 }
27015
27016 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27017 {
27018     node: true, // all tags
27019      
27020      
27021     removeAttributes : function(attr)
27022     {
27023         var ar = this.node.querySelectorAll('*[' + attr + ']');
27024         for (var i =0;i<ar.length;i++) {
27025             ar[i].removeAttribute(attr);
27026         }
27027     }
27028         
27029         
27030         
27031     
27032 });
27033 /***
27034  * This is based loosely on tinymce 
27035  * @class Roo.htmleditor.TidySerializer
27036  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27037  * @constructor
27038  * @method Serializer
27039  * @param {Object} settings Name/value settings object.
27040  */
27041
27042
27043 Roo.htmleditor.TidySerializer = function(settings)
27044 {
27045     Roo.apply(this, settings);
27046     
27047     this.writer = new Roo.htmleditor.TidyWriter(settings);
27048     
27049     
27050
27051 };
27052 Roo.htmleditor.TidySerializer.prototype = {
27053     
27054     /**
27055      * @param {boolean} inner do the inner of the node.
27056      */
27057     inner : false,
27058     
27059     writer : false,
27060     
27061     /**
27062     * Serializes the specified node into a string.
27063     *
27064     * @example
27065     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27066     * @method serialize
27067     * @param {DomElement} node Node instance to serialize.
27068     * @return {String} String with HTML based on DOM tree.
27069     */
27070     serialize : function(node) {
27071         
27072         // = settings.validate;
27073         var writer = this.writer;
27074         var self  = this;
27075         this.handlers = {
27076             // #text
27077             3: function(node) {
27078                 
27079                 writer.text(node.nodeValue, node);
27080             },
27081             // #comment
27082             8: function(node) {
27083                 writer.comment(node.nodeValue);
27084             },
27085             // Processing instruction
27086             7: function(node) {
27087                 writer.pi(node.name, node.nodeValue);
27088             },
27089             // Doctype
27090             10: function(node) {
27091                 writer.doctype(node.nodeValue);
27092             },
27093             // CDATA
27094             4: function(node) {
27095                 writer.cdata(node.nodeValue);
27096             },
27097             // Document fragment
27098             11: function(node) {
27099                 node = node.firstChild;
27100                 if (!node) {
27101                     return;
27102                 }
27103                 while(node) {
27104                     self.walk(node);
27105                     node = node.nextSibling
27106                 }
27107             }
27108         };
27109         writer.reset();
27110         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27111         return writer.getContent();
27112     },
27113
27114     walk: function(node)
27115     {
27116         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27117             handler = this.handlers[node.nodeType];
27118             
27119         if (handler) {
27120             handler(node);
27121             return;
27122         }
27123     
27124         var name = node.nodeName;
27125         var isEmpty = node.childNodes.length < 1;
27126       
27127         var writer = this.writer;
27128         var attrs = node.attributes;
27129         // Sort attributes
27130         
27131         writer.start(node.nodeName, attrs, isEmpty, node);
27132         if (isEmpty) {
27133             return;
27134         }
27135         node = node.firstChild;
27136         if (!node) {
27137             writer.end(name);
27138             return;
27139         }
27140         while (node) {
27141             this.walk(node);
27142             node = node.nextSibling;
27143         }
27144         writer.end(name);
27145         
27146     
27147     }
27148     // Serialize element and treat all non elements as fragments
27149    
27150 }; 
27151
27152 /***
27153  * This is based loosely on tinymce 
27154  * @class Roo.htmleditor.TidyWriter
27155  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27156  *
27157  * Known issues?
27158  * - not tested much with 'PRE' formated elements.
27159  * 
27160  *
27161  *
27162  */
27163
27164 Roo.htmleditor.TidyWriter = function(settings)
27165 {
27166     
27167     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27168     Roo.apply(this, settings);
27169     this.html = [];
27170     this.state = [];
27171      
27172     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27173   
27174 }
27175 Roo.htmleditor.TidyWriter.prototype = {
27176
27177  
27178     state : false,
27179     
27180     indent :  '  ',
27181     
27182     // part of state...
27183     indentstr : '',
27184     in_pre: false,
27185     in_inline : false,
27186     last_inline : false,
27187     encode : false,
27188      
27189     
27190             /**
27191     * Writes the a start element such as <p id="a">.
27192     *
27193     * @method start
27194     * @param {String} name Name of the element.
27195     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27196     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27197     */
27198     start: function(name, attrs, empty, node)
27199     {
27200         var i, l, attr, value;
27201         
27202         // there are some situations where adding line break && indentation will not work. will not work.
27203         // <span / b / i ... formating?
27204         
27205         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27206         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27207         
27208         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27209         
27210         var add_lb = name == 'BR' ? false : in_inline;
27211         
27212         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27213             i_inline = false;
27214         }
27215
27216         var indentstr =  this.indentstr;
27217         
27218         // e_inline = elements that can be inline, but still allow \n before and after?
27219         // only 'BR' ??? any others?
27220         
27221         // ADD LINE BEFORE tage
27222         if (!this.in_pre) {
27223             if (in_inline) {
27224                 //code
27225                 if (name == 'BR') {
27226                     this.addLine();
27227                 } else if (this.lastElementEndsWS()) {
27228                     this.addLine();
27229                 } else{
27230                     // otherwise - no new line. (and dont indent.)
27231                     indentstr = '';
27232                 }
27233                 
27234             } else {
27235                 this.addLine();
27236             }
27237         } else {
27238             indentstr = '';
27239         }
27240         
27241         this.html.push(indentstr + '<', name.toLowerCase());
27242         
27243         if (attrs) {
27244             for (i = 0, l = attrs.length; i < l; i++) {
27245                 attr = attrs[i];
27246                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27247             }
27248         }
27249      
27250         if (empty) {
27251             if (is_short) {
27252                 this.html[this.html.length] = '/>';
27253             } else {
27254                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27255             }
27256             var e_inline = name == 'BR' ? false : this.in_inline;
27257             
27258             if (!e_inline && !this.in_pre) {
27259                 this.addLine();
27260             }
27261             return;
27262         
27263         }
27264         // not empty..
27265         this.html[this.html.length] = '>';
27266         
27267         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27268         /*
27269         if (!in_inline && !in_pre) {
27270             var cn = node.firstChild;
27271             while(cn) {
27272                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27273                     in_inline = true
27274                     break;
27275                 }
27276                 cn = cn.nextSibling;
27277             }
27278              
27279         }
27280         */
27281         
27282         
27283         this.pushState({
27284             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27285             in_pre : in_pre,
27286             in_inline :  in_inline
27287         });
27288         // add a line after if we are not in a
27289         
27290         if (!in_inline && !in_pre) {
27291             this.addLine();
27292         }
27293         
27294             
27295          
27296         
27297     },
27298     
27299     lastElementEndsWS : function()
27300     {
27301         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27302         if (value === false) {
27303             return true;
27304         }
27305         return value.match(/\s+$/);
27306         
27307     },
27308     
27309     /**
27310      * Writes the a end element such as </p>.
27311      *
27312      * @method end
27313      * @param {String} name Name of the element.
27314      */
27315     end: function(name) {
27316         var value;
27317         this.popState();
27318         var indentstr = '';
27319         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27320         
27321         if (!this.in_pre && !in_inline) {
27322             this.addLine();
27323             indentstr  = this.indentstr;
27324         }
27325         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27326         this.last_inline = in_inline;
27327         
27328         // pop the indent state..
27329     },
27330     /**
27331      * Writes a text node.
27332      *
27333      * In pre - we should not mess with the contents.
27334      * 
27335      *
27336      * @method text
27337      * @param {String} text String to write out.
27338      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27339      */
27340     text: function(in_text, node)
27341     {
27342         // if not in whitespace critical
27343         if (in_text.length < 1) {
27344             return;
27345         }
27346         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27347         
27348         if (this.in_pre) {
27349             this.html[this.html.length] =  text;
27350             return;   
27351         }
27352         
27353         if (this.in_inline) {
27354             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27355             if (text != ' ') {
27356                 text = text.replace(/\s+/,' ');  // all white space to single white space
27357                 
27358                     
27359                 // if next tag is '<BR>', then we can trim right..
27360                 if (node.nextSibling &&
27361                     node.nextSibling.nodeType == 1 &&
27362                     node.nextSibling.nodeName == 'BR' )
27363                 {
27364                     text = text.replace(/\s+$/g,'');
27365                 }
27366                 // if previous tag was a BR, we can also trim..
27367                 if (node.previousSibling &&
27368                     node.previousSibling.nodeType == 1 &&
27369                     node.previousSibling.nodeName == 'BR' )
27370                 {
27371                     text = this.indentstr +  text.replace(/^\s+/g,'');
27372                 }
27373                 if (text.match(/\n/)) {
27374                     text = text.replace(
27375                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27376                     );
27377                     // remoeve the last whitespace / line break.
27378                     text = text.replace(/\n\s+$/,'');
27379                 }
27380                 // repace long lines
27381                 
27382             }
27383              
27384             this.html[this.html.length] =  text;
27385             return;   
27386         }
27387         // see if previous element was a inline element.
27388         var indentstr = this.indentstr;
27389    
27390         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27391         
27392         // should trim left?
27393         if (node.previousSibling &&
27394             node.previousSibling.nodeType == 1 &&
27395             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27396         {
27397             indentstr = '';
27398             
27399         } else {
27400             this.addLine();
27401             text = text.replace(/^\s+/,''); // trim left
27402           
27403         }
27404         // should trim right?
27405         if (node.nextSibling &&
27406             node.nextSibling.nodeType == 1 &&
27407             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27408         {
27409           // noop
27410             
27411         }  else {
27412             text = text.replace(/\s+$/,''); // trim right
27413         }
27414          
27415               
27416         
27417         
27418         
27419         if (text.length < 1) {
27420             return;
27421         }
27422         if (!text.match(/\n/)) {
27423             this.html.push(indentstr + text);
27424             return;
27425         }
27426         
27427         text = this.indentstr + text.replace(
27428             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27429         );
27430         // remoeve the last whitespace / line break.
27431         text = text.replace(/\s+$/,''); 
27432         
27433         this.html.push(text);
27434         
27435         // split and indent..
27436         
27437         
27438     },
27439     /**
27440      * Writes a cdata node such as <![CDATA[data]]>.
27441      *
27442      * @method cdata
27443      * @param {String} text String to write out inside the cdata.
27444      */
27445     cdata: function(text) {
27446         this.html.push('<![CDATA[', text, ']]>');
27447     },
27448     /**
27449     * Writes a comment node such as <!-- Comment -->.
27450     *
27451     * @method cdata
27452     * @param {String} text String to write out inside the comment.
27453     */
27454    comment: function(text) {
27455        this.html.push('<!--', text, '-->');
27456    },
27457     /**
27458      * Writes a PI node such as <?xml attr="value" ?>.
27459      *
27460      * @method pi
27461      * @param {String} name Name of the pi.
27462      * @param {String} text String to write out inside the pi.
27463      */
27464     pi: function(name, text) {
27465         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27466         this.indent != '' && this.html.push('\n');
27467     },
27468     /**
27469      * Writes a doctype node such as <!DOCTYPE data>.
27470      *
27471      * @method doctype
27472      * @param {String} text String to write out inside the doctype.
27473      */
27474     doctype: function(text) {
27475         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27476     },
27477     /**
27478      * Resets the internal buffer if one wants to reuse the writer.
27479      *
27480      * @method reset
27481      */
27482     reset: function() {
27483         this.html.length = 0;
27484         this.state = [];
27485         this.pushState({
27486             indentstr : '',
27487             in_pre : false, 
27488             in_inline : false
27489         })
27490     },
27491     /**
27492      * Returns the contents that got serialized.
27493      *
27494      * @method getContent
27495      * @return {String} HTML contents that got written down.
27496      */
27497     getContent: function() {
27498         return this.html.join('').replace(/\n$/, '');
27499     },
27500     
27501     pushState : function(cfg)
27502     {
27503         this.state.push(cfg);
27504         Roo.apply(this, cfg);
27505     },
27506     
27507     popState : function()
27508     {
27509         if (this.state.length < 1) {
27510             return; // nothing to push
27511         }
27512         var cfg = {
27513             in_pre: false,
27514             indentstr : ''
27515         };
27516         this.state.pop();
27517         if (this.state.length > 0) {
27518             cfg = this.state[this.state.length-1]; 
27519         }
27520         Roo.apply(this, cfg);
27521     },
27522     
27523     addLine: function()
27524     {
27525         if (this.html.length < 1) {
27526             return;
27527         }
27528         
27529         
27530         var value = this.html[this.html.length - 1];
27531         if (value.length > 0 && '\n' !== value) {
27532             this.html.push('\n');
27533         }
27534     }
27535     
27536     
27537 //'pre script noscript style textarea video audio iframe object code'
27538 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
27539 // inline 
27540 };
27541
27542 Roo.htmleditor.TidyWriter.inline_elements = [
27543         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
27544         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
27545 ];
27546 Roo.htmleditor.TidyWriter.shortend_elements = [
27547     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
27548     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
27549 ];
27550
27551 Roo.htmleditor.TidyWriter.whitespace_elements = [
27552     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
27553 ];/***
27554  * This is based loosely on tinymce 
27555  * @class Roo.htmleditor.TidyEntities
27556  * @static
27557  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27558  *
27559  * Not 100% sure this is actually used or needed.
27560  */
27561
27562 Roo.htmleditor.TidyEntities = {
27563     
27564     /**
27565      * initialize data..
27566      */
27567     init : function (){
27568      
27569         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
27570        
27571     },
27572
27573
27574     buildEntitiesLookup: function(items, radix) {
27575         var i, chr, entity, lookup = {};
27576         if (!items) {
27577             return {};
27578         }
27579         items = typeof(items) == 'string' ? items.split(',') : items;
27580         radix = radix || 10;
27581         // Build entities lookup table
27582         for (i = 0; i < items.length; i += 2) {
27583             chr = String.fromCharCode(parseInt(items[i], radix));
27584             // Only add non base entities
27585             if (!this.baseEntities[chr]) {
27586                 entity = '&' + items[i + 1] + ';';
27587                 lookup[chr] = entity;
27588                 lookup[entity] = chr;
27589             }
27590         }
27591         return lookup;
27592         
27593     },
27594     
27595     asciiMap : {
27596             128: '€',
27597             130: '‚',
27598             131: 'ƒ',
27599             132: '„',
27600             133: '…',
27601             134: '†',
27602             135: '‡',
27603             136: 'ˆ',
27604             137: '‰',
27605             138: 'Š',
27606             139: '‹',
27607             140: 'Œ',
27608             142: 'Ž',
27609             145: '‘',
27610             146: '’',
27611             147: '“',
27612             148: '”',
27613             149: '•',
27614             150: '–',
27615             151: '—',
27616             152: '˜',
27617             153: '™',
27618             154: 'š',
27619             155: '›',
27620             156: 'œ',
27621             158: 'ž',
27622             159: 'Ÿ'
27623     },
27624     // Raw entities
27625     baseEntities : {
27626         '"': '&quot;',
27627         // Needs to be escaped since the YUI compressor would otherwise break the code
27628         '\'': '&#39;',
27629         '<': '&lt;',
27630         '>': '&gt;',
27631         '&': '&amp;',
27632         '`': '&#96;'
27633     },
27634     // Reverse lookup table for raw entities
27635     reverseEntities : {
27636         '&lt;': '<',
27637         '&gt;': '>',
27638         '&amp;': '&',
27639         '&quot;': '"',
27640         '&apos;': '\''
27641     },
27642     
27643     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27644     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27645     rawCharsRegExp : /[<>&\"\']/g,
27646     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
27647     namedEntities  : false,
27648     namedEntitiesData : [ 
27649         '50',
27650         'nbsp',
27651         '51',
27652         'iexcl',
27653         '52',
27654         'cent',
27655         '53',
27656         'pound',
27657         '54',
27658         'curren',
27659         '55',
27660         'yen',
27661         '56',
27662         'brvbar',
27663         '57',
27664         'sect',
27665         '58',
27666         'uml',
27667         '59',
27668         'copy',
27669         '5a',
27670         'ordf',
27671         '5b',
27672         'laquo',
27673         '5c',
27674         'not',
27675         '5d',
27676         'shy',
27677         '5e',
27678         'reg',
27679         '5f',
27680         'macr',
27681         '5g',
27682         'deg',
27683         '5h',
27684         'plusmn',
27685         '5i',
27686         'sup2',
27687         '5j',
27688         'sup3',
27689         '5k',
27690         'acute',
27691         '5l',
27692         'micro',
27693         '5m',
27694         'para',
27695         '5n',
27696         'middot',
27697         '5o',
27698         'cedil',
27699         '5p',
27700         'sup1',
27701         '5q',
27702         'ordm',
27703         '5r',
27704         'raquo',
27705         '5s',
27706         'frac14',
27707         '5t',
27708         'frac12',
27709         '5u',
27710         'frac34',
27711         '5v',
27712         'iquest',
27713         '60',
27714         'Agrave',
27715         '61',
27716         'Aacute',
27717         '62',
27718         'Acirc',
27719         '63',
27720         'Atilde',
27721         '64',
27722         'Auml',
27723         '65',
27724         'Aring',
27725         '66',
27726         'AElig',
27727         '67',
27728         'Ccedil',
27729         '68',
27730         'Egrave',
27731         '69',
27732         'Eacute',
27733         '6a',
27734         'Ecirc',
27735         '6b',
27736         'Euml',
27737         '6c',
27738         'Igrave',
27739         '6d',
27740         'Iacute',
27741         '6e',
27742         'Icirc',
27743         '6f',
27744         'Iuml',
27745         '6g',
27746         'ETH',
27747         '6h',
27748         'Ntilde',
27749         '6i',
27750         'Ograve',
27751         '6j',
27752         'Oacute',
27753         '6k',
27754         'Ocirc',
27755         '6l',
27756         'Otilde',
27757         '6m',
27758         'Ouml',
27759         '6n',
27760         'times',
27761         '6o',
27762         'Oslash',
27763         '6p',
27764         'Ugrave',
27765         '6q',
27766         'Uacute',
27767         '6r',
27768         'Ucirc',
27769         '6s',
27770         'Uuml',
27771         '6t',
27772         'Yacute',
27773         '6u',
27774         'THORN',
27775         '6v',
27776         'szlig',
27777         '70',
27778         'agrave',
27779         '71',
27780         'aacute',
27781         '72',
27782         'acirc',
27783         '73',
27784         'atilde',
27785         '74',
27786         'auml',
27787         '75',
27788         'aring',
27789         '76',
27790         'aelig',
27791         '77',
27792         'ccedil',
27793         '78',
27794         'egrave',
27795         '79',
27796         'eacute',
27797         '7a',
27798         'ecirc',
27799         '7b',
27800         'euml',
27801         '7c',
27802         'igrave',
27803         '7d',
27804         'iacute',
27805         '7e',
27806         'icirc',
27807         '7f',
27808         'iuml',
27809         '7g',
27810         'eth',
27811         '7h',
27812         'ntilde',
27813         '7i',
27814         'ograve',
27815         '7j',
27816         'oacute',
27817         '7k',
27818         'ocirc',
27819         '7l',
27820         'otilde',
27821         '7m',
27822         'ouml',
27823         '7n',
27824         'divide',
27825         '7o',
27826         'oslash',
27827         '7p',
27828         'ugrave',
27829         '7q',
27830         'uacute',
27831         '7r',
27832         'ucirc',
27833         '7s',
27834         'uuml',
27835         '7t',
27836         'yacute',
27837         '7u',
27838         'thorn',
27839         '7v',
27840         'yuml',
27841         'ci',
27842         'fnof',
27843         'sh',
27844         'Alpha',
27845         'si',
27846         'Beta',
27847         'sj',
27848         'Gamma',
27849         'sk',
27850         'Delta',
27851         'sl',
27852         'Epsilon',
27853         'sm',
27854         'Zeta',
27855         'sn',
27856         'Eta',
27857         'so',
27858         'Theta',
27859         'sp',
27860         'Iota',
27861         'sq',
27862         'Kappa',
27863         'sr',
27864         'Lambda',
27865         'ss',
27866         'Mu',
27867         'st',
27868         'Nu',
27869         'su',
27870         'Xi',
27871         'sv',
27872         'Omicron',
27873         't0',
27874         'Pi',
27875         't1',
27876         'Rho',
27877         't3',
27878         'Sigma',
27879         't4',
27880         'Tau',
27881         't5',
27882         'Upsilon',
27883         't6',
27884         'Phi',
27885         't7',
27886         'Chi',
27887         't8',
27888         'Psi',
27889         't9',
27890         'Omega',
27891         'th',
27892         'alpha',
27893         'ti',
27894         'beta',
27895         'tj',
27896         'gamma',
27897         'tk',
27898         'delta',
27899         'tl',
27900         'epsilon',
27901         'tm',
27902         'zeta',
27903         'tn',
27904         'eta',
27905         'to',
27906         'theta',
27907         'tp',
27908         'iota',
27909         'tq',
27910         'kappa',
27911         'tr',
27912         'lambda',
27913         'ts',
27914         'mu',
27915         'tt',
27916         'nu',
27917         'tu',
27918         'xi',
27919         'tv',
27920         'omicron',
27921         'u0',
27922         'pi',
27923         'u1',
27924         'rho',
27925         'u2',
27926         'sigmaf',
27927         'u3',
27928         'sigma',
27929         'u4',
27930         'tau',
27931         'u5',
27932         'upsilon',
27933         'u6',
27934         'phi',
27935         'u7',
27936         'chi',
27937         'u8',
27938         'psi',
27939         'u9',
27940         'omega',
27941         'uh',
27942         'thetasym',
27943         'ui',
27944         'upsih',
27945         'um',
27946         'piv',
27947         '812',
27948         'bull',
27949         '816',
27950         'hellip',
27951         '81i',
27952         'prime',
27953         '81j',
27954         'Prime',
27955         '81u',
27956         'oline',
27957         '824',
27958         'frasl',
27959         '88o',
27960         'weierp',
27961         '88h',
27962         'image',
27963         '88s',
27964         'real',
27965         '892',
27966         'trade',
27967         '89l',
27968         'alefsym',
27969         '8cg',
27970         'larr',
27971         '8ch',
27972         'uarr',
27973         '8ci',
27974         'rarr',
27975         '8cj',
27976         'darr',
27977         '8ck',
27978         'harr',
27979         '8dl',
27980         'crarr',
27981         '8eg',
27982         'lArr',
27983         '8eh',
27984         'uArr',
27985         '8ei',
27986         'rArr',
27987         '8ej',
27988         'dArr',
27989         '8ek',
27990         'hArr',
27991         '8g0',
27992         'forall',
27993         '8g2',
27994         'part',
27995         '8g3',
27996         'exist',
27997         '8g5',
27998         'empty',
27999         '8g7',
28000         'nabla',
28001         '8g8',
28002         'isin',
28003         '8g9',
28004         'notin',
28005         '8gb',
28006         'ni',
28007         '8gf',
28008         'prod',
28009         '8gh',
28010         'sum',
28011         '8gi',
28012         'minus',
28013         '8gn',
28014         'lowast',
28015         '8gq',
28016         'radic',
28017         '8gt',
28018         'prop',
28019         '8gu',
28020         'infin',
28021         '8h0',
28022         'ang',
28023         '8h7',
28024         'and',
28025         '8h8',
28026         'or',
28027         '8h9',
28028         'cap',
28029         '8ha',
28030         'cup',
28031         '8hb',
28032         'int',
28033         '8hk',
28034         'there4',
28035         '8hs',
28036         'sim',
28037         '8i5',
28038         'cong',
28039         '8i8',
28040         'asymp',
28041         '8j0',
28042         'ne',
28043         '8j1',
28044         'equiv',
28045         '8j4',
28046         'le',
28047         '8j5',
28048         'ge',
28049         '8k2',
28050         'sub',
28051         '8k3',
28052         'sup',
28053         '8k4',
28054         'nsub',
28055         '8k6',
28056         'sube',
28057         '8k7',
28058         'supe',
28059         '8kl',
28060         'oplus',
28061         '8kn',
28062         'otimes',
28063         '8l5',
28064         'perp',
28065         '8m5',
28066         'sdot',
28067         '8o8',
28068         'lceil',
28069         '8o9',
28070         'rceil',
28071         '8oa',
28072         'lfloor',
28073         '8ob',
28074         'rfloor',
28075         '8p9',
28076         'lang',
28077         '8pa',
28078         'rang',
28079         '9ea',
28080         'loz',
28081         '9j0',
28082         'spades',
28083         '9j3',
28084         'clubs',
28085         '9j5',
28086         'hearts',
28087         '9j6',
28088         'diams',
28089         'ai',
28090         'OElig',
28091         'aj',
28092         'oelig',
28093         'b0',
28094         'Scaron',
28095         'b1',
28096         'scaron',
28097         'bo',
28098         'Yuml',
28099         'm6',
28100         'circ',
28101         'ms',
28102         'tilde',
28103         '802',
28104         'ensp',
28105         '803',
28106         'emsp',
28107         '809',
28108         'thinsp',
28109         '80c',
28110         'zwnj',
28111         '80d',
28112         'zwj',
28113         '80e',
28114         'lrm',
28115         '80f',
28116         'rlm',
28117         '80j',
28118         'ndash',
28119         '80k',
28120         'mdash',
28121         '80o',
28122         'lsquo',
28123         '80p',
28124         'rsquo',
28125         '80q',
28126         'sbquo',
28127         '80s',
28128         'ldquo',
28129         '80t',
28130         'rdquo',
28131         '80u',
28132         'bdquo',
28133         '810',
28134         'dagger',
28135         '811',
28136         'Dagger',
28137         '81g',
28138         'permil',
28139         '81p',
28140         'lsaquo',
28141         '81q',
28142         'rsaquo',
28143         '85c',
28144         'euro'
28145     ],
28146
28147          
28148     /**
28149      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28150      *
28151      * @method encodeRaw
28152      * @param {String} text Text to encode.
28153      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28154      * @return {String} Entity encoded text.
28155      */
28156     encodeRaw: function(text, attr)
28157     {
28158         var t = this;
28159         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28160             return t.baseEntities[chr] || chr;
28161         });
28162     },
28163     /**
28164      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28165      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28166      * and is exposed as the DOMUtils.encode function.
28167      *
28168      * @method encodeAllRaw
28169      * @param {String} text Text to encode.
28170      * @return {String} Entity encoded text.
28171      */
28172     encodeAllRaw: function(text) {
28173         var t = this;
28174         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28175             return t.baseEntities[chr] || chr;
28176         });
28177     },
28178     /**
28179      * Encodes the specified string using numeric entities. The core entities will be
28180      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28181      *
28182      * @method encodeNumeric
28183      * @param {String} text Text to encode.
28184      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28185      * @return {String} Entity encoded text.
28186      */
28187     encodeNumeric: function(text, attr) {
28188         var t = this;
28189         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28190             // Multi byte sequence convert it to a single entity
28191             if (chr.length > 1) {
28192                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28193             }
28194             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28195         });
28196     },
28197     /**
28198      * Encodes the specified string using named entities. The core entities will be encoded
28199      * as named ones but all non lower ascii characters will be encoded into named entities.
28200      *
28201      * @method encodeNamed
28202      * @param {String} text Text to encode.
28203      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28204      * @param {Object} entities Optional parameter with entities to use.
28205      * @return {String} Entity encoded text.
28206      */
28207     encodeNamed: function(text, attr, entities) {
28208         var t = this;
28209         entities = entities || this.namedEntities;
28210         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28211             return t.baseEntities[chr] || entities[chr] || chr;
28212         });
28213     },
28214     /**
28215      * Returns an encode function based on the name(s) and it's optional entities.
28216      *
28217      * @method getEncodeFunc
28218      * @param {String} name Comma separated list of encoders for example named,numeric.
28219      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28220      * @return {function} Encode function to be used.
28221      */
28222     getEncodeFunc: function(name, entities) {
28223         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28224         var t = this;
28225         function encodeNamedAndNumeric(text, attr) {
28226             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28227                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28228             });
28229         }
28230
28231         function encodeCustomNamed(text, attr) {
28232             return t.encodeNamed(text, attr, entities);
28233         }
28234         // Replace + with , to be compatible with previous TinyMCE versions
28235         name = this.makeMap(name.replace(/\+/g, ','));
28236         // Named and numeric encoder
28237         if (name.named && name.numeric) {
28238             return this.encodeNamedAndNumeric;
28239         }
28240         // Named encoder
28241         if (name.named) {
28242             // Custom names
28243             if (entities) {
28244                 return encodeCustomNamed;
28245             }
28246             return this.encodeNamed;
28247         }
28248         // Numeric
28249         if (name.numeric) {
28250             return this.encodeNumeric;
28251         }
28252         // Raw encoder
28253         return this.encodeRaw;
28254     },
28255     /**
28256      * Decodes the specified string, this will replace entities with raw UTF characters.
28257      *
28258      * @method decode
28259      * @param {String} text Text to entity decode.
28260      * @return {String} Entity decoded string.
28261      */
28262     decode: function(text)
28263     {
28264         var  t = this;
28265         return text.replace(this.entityRegExp, function(all, numeric) {
28266             if (numeric) {
28267                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28268                 // Support upper UTF
28269                 if (numeric > 65535) {
28270                     numeric -= 65536;
28271                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28272                 }
28273                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28274             }
28275             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28276         });
28277     },
28278     nativeDecode : function (text) {
28279         return text;
28280     },
28281     makeMap : function (items, delim, map) {
28282                 var i;
28283                 items = items || [];
28284                 delim = delim || ',';
28285                 if (typeof items == "string") {
28286                         items = items.split(delim);
28287                 }
28288                 map = map || {};
28289                 i = items.length;
28290                 while (i--) {
28291                         map[items[i]] = {};
28292                 }
28293                 return map;
28294         }
28295 };
28296     
28297     
28298     
28299 Roo.htmleditor.TidyEntities.init();
28300 /**
28301  * @class Roo.htmleditor.KeyEnter
28302  * Handle Enter press..
28303  * @cfg {Roo.HtmlEditorCore} core the editor.
28304  * @constructor
28305  * Create a new Filter.
28306  * @param {Object} config Configuration options
28307  */
28308
28309
28310
28311
28312
28313 Roo.htmleditor.KeyEnter = function(cfg) {
28314     Roo.apply(this, cfg);
28315     // this does not actually call walk as it's really just a abstract class
28316  
28317     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28318 }
28319
28320 //Roo.htmleditor.KeyEnter.i = 0;
28321
28322
28323 Roo.htmleditor.KeyEnter.prototype = {
28324     
28325     core : false,
28326     
28327     keypress : function(e)
28328     {
28329         if (e.charCode != 13 && e.charCode != 10) {
28330             Roo.log([e.charCode,e]);
28331             return true;
28332         }
28333         e.preventDefault();
28334         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28335         var doc = this.core.doc;
28336           //add a new line
28337        
28338     
28339         var sel = this.core.getSelection();
28340         var range = sel.getRangeAt(0);
28341         var n = range.commonAncestorContainer;
28342         var pc = range.closest([ 'ol', 'ul']);
28343         var pli = range.closest('li');
28344         if (!pc || e.ctrlKey) {
28345             // on it list, or ctrl pressed.
28346             if (!e.ctrlKey) {
28347                 sel.insertNode('br', 'after'); 
28348             } else {
28349                 // only do this if we have ctrl key..
28350                 var br = doc.createElement('br');
28351                 br.className = 'clear';
28352                 br.setAttribute('style', 'clear: both');
28353                 sel.insertNode(br, 'after'); 
28354             }
28355             
28356          
28357             this.core.undoManager.addEvent();
28358             this.core.fireEditorEvent(e);
28359             return false;
28360         }
28361         
28362         // deal with <li> insetion
28363         if (pli.innerText.trim() == '' &&
28364             pli.previousSibling &&
28365             pli.previousSibling.nodeName == 'LI' &&
28366             pli.previousSibling.innerText.trim() ==  '') {
28367             pli.parentNode.removeChild(pli.previousSibling);
28368             sel.cursorAfter(pc);
28369             this.core.undoManager.addEvent();
28370             this.core.fireEditorEvent(e);
28371             return false;
28372         }
28373     
28374         var li = doc.createElement('LI');
28375         li.innerHTML = '&nbsp;';
28376         if (!pli || !pli.firstSibling) {
28377             pc.appendChild(li);
28378         } else {
28379             pli.parentNode.insertBefore(li, pli.firstSibling);
28380         }
28381         sel.cursorText (li.firstChild);
28382       
28383         this.core.undoManager.addEvent();
28384         this.core.fireEditorEvent(e);
28385
28386         return false;
28387         
28388     
28389         
28390         
28391          
28392     }
28393 };
28394      
28395 /**
28396  * @class Roo.htmleditor.Block
28397  * Base class for html editor blocks - do not use it directly .. extend it..
28398  * @cfg {DomElement} node The node to apply stuff to.
28399  * @cfg {String} friendly_name the name that appears in the context bar about this block
28400  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28401  
28402  * @constructor
28403  * Create a new Filter.
28404  * @param {Object} config Configuration options
28405  */
28406
28407 Roo.htmleditor.Block  = function(cfg)
28408 {
28409     // do nothing .. should not be called really.
28410 }
28411 /**
28412  * factory method to get the block from an element (using cache if necessary)
28413  * @static
28414  * @param {HtmlElement} the dom element
28415  */
28416 Roo.htmleditor.Block.factory = function(node)
28417 {
28418     var cc = Roo.htmleditor.Block.cache;
28419     var id = Roo.get(node).id;
28420     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28421         Roo.htmleditor.Block.cache[id].readElement(node);
28422         return Roo.htmleditor.Block.cache[id];
28423     }
28424     var db  = node.getAttribute('data-block');
28425     if (!db) {
28426         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28427     }
28428     var cls = Roo.htmleditor['Block' + db];
28429     if (typeof(cls) == 'undefined') {
28430         //Roo.log(node.getAttribute('data-block'));
28431         Roo.log("OOps missing block : " + 'Block' + db);
28432         return false;
28433     }
28434     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28435     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
28436 };
28437
28438 /**
28439  * initalize all Elements from content that are 'blockable'
28440  * @static
28441  * @param the body element
28442  */
28443 Roo.htmleditor.Block.initAll = function(body, type)
28444 {
28445     if (typeof(type) == 'undefined') {
28446         var ia = Roo.htmleditor.Block.initAll;
28447         ia(body,'table');
28448         ia(body,'td');
28449         ia(body,'figure');
28450         return;
28451     }
28452     Roo.each(Roo.get(body).query(type), function(e) {
28453         Roo.htmleditor.Block.factory(e);    
28454     },this);
28455 };
28456 // question goes here... do we need to clear out this cache sometimes?
28457 // or show we make it relivant to the htmleditor.
28458 Roo.htmleditor.Block.cache = {};
28459
28460 Roo.htmleditor.Block.prototype = {
28461     
28462     node : false,
28463     
28464      // used by context menu
28465     friendly_name : 'Based Block',
28466     
28467     // text for button to delete this element
28468     deleteTitle : false,
28469     
28470     context : false,
28471     /**
28472      * Update a node with values from this object
28473      * @param {DomElement} node
28474      */
28475     updateElement : function(node)
28476     {
28477         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28478     },
28479      /**
28480      * convert to plain HTML for calling insertAtCursor..
28481      */
28482     toHTML : function()
28483     {
28484         return Roo.DomHelper.markup(this.toObject());
28485     },
28486     /**
28487      * used by readEleemnt to extract data from a node
28488      * may need improving as it's pretty basic
28489      
28490      * @param {DomElement} node
28491      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28492      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28493      * @param {String} style the style property - eg. text-align
28494      */
28495     getVal : function(node, tag, attr, style)
28496     {
28497         var n = node;
28498         if (tag !== true && n.tagName != tag.toUpperCase()) {
28499             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28500             // but kiss for now.
28501             n = node.getElementsByTagName(tag).item(0);
28502         }
28503         if (!n) {
28504             return '';
28505         }
28506         if (attr === false) {
28507             return n;
28508         }
28509         if (attr == 'html') {
28510             return n.innerHTML;
28511         }
28512         if (attr == 'style') {
28513             return n.style[style]; 
28514         }
28515         
28516         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
28517             
28518     },
28519     /**
28520      * create a DomHelper friendly object - for use with 
28521      * Roo.DomHelper.markup / overwrite / etc..
28522      * (override this)
28523      */
28524     toObject : function()
28525     {
28526         return {};
28527     },
28528       /**
28529      * Read a node that has a 'data-block' property - and extract the values from it.
28530      * @param {DomElement} node - the node
28531      */
28532     readElement : function(node)
28533     {
28534         
28535     } 
28536     
28537     
28538 };
28539
28540  
28541
28542 /**
28543  * @class Roo.htmleditor.BlockFigure
28544  * Block that has an image and a figcaption
28545  * @cfg {String} image_src the url for the image
28546  * @cfg {String} align (left|right) alignment for the block default left
28547  * @cfg {String} caption the text to appear below  (and in the alt tag)
28548  * @cfg {String} caption_display (block|none) display or not the caption
28549  * @cfg {String|number} image_width the width of the image number or %?
28550  * @cfg {String|number} image_height the height of the image number or %?
28551  * 
28552  * @constructor
28553  * Create a new Filter.
28554  * @param {Object} config Configuration options
28555  */
28556
28557 Roo.htmleditor.BlockFigure = function(cfg)
28558 {
28559     if (cfg.node) {
28560         this.readElement(cfg.node);
28561         this.updateElement(cfg.node);
28562     }
28563     Roo.apply(this, cfg);
28564 }
28565 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
28566  
28567     
28568     // setable values.
28569     image_src: '',
28570     align: 'center',
28571     caption : '',
28572     caption_display : 'block',
28573     width : '100%',
28574     cls : '',
28575     href: '',
28576     video_url : '',
28577     
28578     // margin: '2%', not used
28579     
28580     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
28581
28582     
28583     // used by context menu
28584     friendly_name : 'Image with caption',
28585     deleteTitle : "Delete Image and Caption",
28586     
28587     contextMenu : function(toolbar)
28588     {
28589         
28590         var block = function() {
28591             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28592         };
28593         
28594         
28595         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28596         
28597         var syncValue = toolbar.editorcore.syncValue;
28598         
28599         var fields = {};
28600         
28601         return [
28602              {
28603                 xtype : 'TextItem',
28604                 text : "Source: ",
28605                 xns : rooui.Toolbar  //Boostrap?
28606             },
28607             {
28608                 xtype : 'Button',
28609                 text: 'Change Image URL',
28610                  
28611                 listeners : {
28612                     click: function (btn, state)
28613                     {
28614                         var b = block();
28615                         
28616                         Roo.MessageBox.show({
28617                             title : "Image Source URL",
28618                             msg : "Enter the url for the image",
28619                             buttons: Roo.MessageBox.OKCANCEL,
28620                             fn: function(btn, val){
28621                                 if (btn != 'ok') {
28622                                     return;
28623                                 }
28624                                 b.image_src = val;
28625                                 b.updateElement();
28626                                 syncValue();
28627                                 toolbar.editorcore.onEditorEvent();
28628                             },
28629                             minWidth:250,
28630                             prompt:true,
28631                             //multiline: multiline,
28632                             modal : true,
28633                             value : b.image_src
28634                         });
28635                     }
28636                 },
28637                 xns : rooui.Toolbar
28638             },
28639          
28640             {
28641                 xtype : 'Button',
28642                 text: 'Change Link URL',
28643                  
28644                 listeners : {
28645                     click: function (btn, state)
28646                     {
28647                         var b = block();
28648                         
28649                         Roo.MessageBox.show({
28650                             title : "Link URL",
28651                             msg : "Enter the url for the link - leave blank to have no link",
28652                             buttons: Roo.MessageBox.OKCANCEL,
28653                             fn: function(btn, val){
28654                                 if (btn != 'ok') {
28655                                     return;
28656                                 }
28657                                 b.href = val;
28658                                 b.updateElement();
28659                                 syncValue();
28660                                 toolbar.editorcore.onEditorEvent();
28661                             },
28662                             minWidth:250,
28663                             prompt:true,
28664                             //multiline: multiline,
28665                             modal : true,
28666                             value : b.href
28667                         });
28668                     }
28669                 },
28670                 xns : rooui.Toolbar
28671             },
28672             {
28673                 xtype : 'Button',
28674                 text: 'Show Video URL',
28675                  
28676                 listeners : {
28677                     click: function (btn, state)
28678                     {
28679                         Roo.MessageBox.alert("Video URL",
28680                             block().video_url == '' ? 'This image is not linked ot a video' :
28681                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
28682                     }
28683                 },
28684                 xns : rooui.Toolbar
28685             },
28686             
28687             
28688             {
28689                 xtype : 'TextItem',
28690                 text : "Width: ",
28691                 xns : rooui.Toolbar  //Boostrap?
28692             },
28693             {
28694                 xtype : 'ComboBox',
28695                 allowBlank : false,
28696                 displayField : 'val',
28697                 editable : true,
28698                 listWidth : 100,
28699                 triggerAction : 'all',
28700                 typeAhead : true,
28701                 valueField : 'val',
28702                 width : 70,
28703                 name : 'width',
28704                 listeners : {
28705                     select : function (combo, r, index)
28706                     {
28707                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28708                         var b = block();
28709                         b.width = r.get('val');
28710                         b.updateElement();
28711                         syncValue();
28712                         toolbar.editorcore.onEditorEvent();
28713                     }
28714                 },
28715                 xns : rooui.form,
28716                 store : {
28717                     xtype : 'SimpleStore',
28718                     data : [
28719                         ['100%'],
28720                         ['80%'],
28721                         ['50%'],
28722                         ['20%'],
28723                         ['10%']
28724                     ],
28725                     fields : [ 'val'],
28726                     xns : Roo.data
28727                 }
28728             },
28729             {
28730                 xtype : 'TextItem',
28731                 text : "Align: ",
28732                 xns : rooui.Toolbar  //Boostrap?
28733             },
28734             {
28735                 xtype : 'ComboBox',
28736                 allowBlank : false,
28737                 displayField : 'val',
28738                 editable : true,
28739                 listWidth : 100,
28740                 triggerAction : 'all',
28741                 typeAhead : true,
28742                 valueField : 'val',
28743                 width : 70,
28744                 name : 'align',
28745                 listeners : {
28746                     select : function (combo, r, index)
28747                     {
28748                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28749                         var b = block();
28750                         b.align = r.get('val');
28751                         b.updateElement();
28752                         syncValue();
28753                         toolbar.editorcore.onEditorEvent();
28754                     }
28755                 },
28756                 xns : rooui.form,
28757                 store : {
28758                     xtype : 'SimpleStore',
28759                     data : [
28760                         ['left'],
28761                         ['right'],
28762                         ['center']
28763                     ],
28764                     fields : [ 'val'],
28765                     xns : Roo.data
28766                 }
28767             },
28768             
28769             
28770             {
28771                 xtype : 'Button',
28772                 text: 'Hide Caption',
28773                 name : 'caption_display',
28774                 pressed : false,
28775                 enableToggle : true,
28776                 setValue : function(v) {
28777                     // this trigger toggle.
28778                      
28779                     this.setText(v ? "Hide Caption" : "Show Caption");
28780                     this.setPressed(v != 'block');
28781                 },
28782                 listeners : {
28783                     toggle: function (btn, state)
28784                     {
28785                         var b  = block();
28786                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
28787                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
28788                         b.updateElement();
28789                         syncValue();
28790                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28791                         toolbar.editorcore.onEditorEvent();
28792                     }
28793                 },
28794                 xns : rooui.Toolbar
28795             }
28796         ];
28797         
28798     },
28799     /**
28800      * create a DomHelper friendly object - for use with
28801      * Roo.DomHelper.markup / overwrite / etc..
28802      */
28803     toObject : function()
28804     {
28805         var d = document.createElement('div');
28806         d.innerHTML = this.caption;
28807         
28808         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
28809         
28810         var iw = this.align == 'center' ? this.width : '100%';
28811         var img =   {
28812             tag : 'img',
28813             contenteditable : 'false',
28814             src : this.image_src,
28815             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
28816             style: {
28817                 width : iw,
28818                 maxWidth : iw + ' !important', // this is not getting rendered?
28819                 margin : m  
28820                 
28821             }
28822         };
28823         /*
28824         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
28825                     '<a href="{2}">' + 
28826                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
28827                     '</a>' + 
28828                 '</div>',
28829         */
28830                 
28831         if (this.href.length > 0) {
28832             img = {
28833                 tag : 'a',
28834                 href: this.href,
28835                 contenteditable : 'true',
28836                 cn : [
28837                     img
28838                 ]
28839             };
28840         }
28841         
28842         
28843         if (this.video_url.length > 0) {
28844             img = {
28845                 tag : 'div',
28846                 cls : this.cls,
28847                 frameborder : 0,
28848                 allowfullscreen : true,
28849                 width : 420,  // these are for video tricks - that we replace the outer
28850                 height : 315,
28851                 src : this.video_url,
28852                 cn : [
28853                     img
28854                 ]
28855             };
28856         }
28857         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
28858         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
28859         
28860   
28861         var ret =   {
28862             tag: 'figure',
28863             'data-block' : 'Figure',
28864             'data-width' : this.width, 
28865             contenteditable : 'false',
28866             
28867             style : {
28868                 display: 'block',
28869                 float :  this.align ,
28870                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
28871                 width : this.align == 'center' ? '100%' : this.width,
28872                 margin:  '0px',
28873                 padding: this.align == 'center' ? '0' : '0 10px' ,
28874                 textAlign : this.align   // seems to work for email..
28875                 
28876             },
28877            
28878             
28879             align : this.align,
28880             cn : [
28881                 img,
28882               
28883                 {
28884                     tag: 'figcaption',
28885                     'data-display' : this.caption_display,
28886                     style : {
28887                         textAlign : 'left',
28888                         fontSize : '16px',
28889                         lineHeight : '24px',
28890                         display : this.caption_display,
28891                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
28892                         margin: m,
28893                         width: this.align == 'center' ?  this.width : '100%' 
28894                     
28895                          
28896                     },
28897                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
28898                     cn : [
28899                         {
28900                             tag: 'div',
28901                             style  : {
28902                                 marginTop : '16px',
28903                                 textAlign : 'left'
28904                             },
28905                             align: 'left',
28906                             cn : [
28907                                 {
28908                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
28909                                     tag : 'i',
28910                                     contenteditable : true,
28911                                     html : captionhtml
28912                                 }
28913                                 
28914                             ]
28915                         }
28916                         
28917                     ]
28918                     
28919                 }
28920             ]
28921         };
28922         return ret;
28923          
28924     },
28925     
28926     readElement : function(node)
28927     {
28928         // this should not really come from the link...
28929         this.video_url = this.getVal(node, 'div', 'src');
28930         this.cls = this.getVal(node, 'div', 'class');
28931         this.href = this.getVal(node, 'a', 'href');
28932         
28933         
28934         this.image_src = this.getVal(node, 'img', 'src');
28935          
28936         this.align = this.getVal(node, 'figure', 'align');
28937         var figcaption = this.getVal(node, 'figcaption', false);
28938         if (figcaption !== '') {
28939             this.caption = this.getVal(figcaption, 'i', 'html');
28940         }
28941         
28942
28943         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
28944         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
28945         this.width = this.getVal(node, true, 'data-width');
28946         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
28947         
28948     },
28949     removeNode : function()
28950     {
28951         return this.node;
28952     }
28953     
28954   
28955    
28956      
28957     
28958     
28959     
28960     
28961 })
28962
28963  
28964
28965 /**
28966  * @class Roo.htmleditor.BlockTable
28967  * Block that manages a table
28968  * 
28969  * @constructor
28970  * Create a new Filter.
28971  * @param {Object} config Configuration options
28972  */
28973
28974 Roo.htmleditor.BlockTable = function(cfg)
28975 {
28976     if (cfg.node) {
28977         this.readElement(cfg.node);
28978         this.updateElement(cfg.node);
28979     }
28980     Roo.apply(this, cfg);
28981     if (!cfg.node) {
28982         this.rows = [];
28983         for(var r = 0; r < this.no_row; r++) {
28984             this.rows[r] = [];
28985             for(var c = 0; c < this.no_col; c++) {
28986                 this.rows[r][c] = this.emptyCell();
28987             }
28988         }
28989     }
28990     
28991     
28992 }
28993 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
28994  
28995     rows : false,
28996     no_col : 1,
28997     no_row : 1,
28998     
28999     
29000     width: '100%',
29001     
29002     // used by context menu
29003     friendly_name : 'Table',
29004     deleteTitle : 'Delete Table',
29005     // context menu is drawn once..
29006     
29007     contextMenu : function(toolbar)
29008     {
29009         
29010         var block = function() {
29011             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29012         };
29013         
29014         
29015         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29016         
29017         var syncValue = toolbar.editorcore.syncValue;
29018         
29019         var fields = {};
29020         
29021         return [
29022             {
29023                 xtype : 'TextItem',
29024                 text : "Width: ",
29025                 xns : rooui.Toolbar  //Boostrap?
29026             },
29027             {
29028                 xtype : 'ComboBox',
29029                 allowBlank : false,
29030                 displayField : 'val',
29031                 editable : true,
29032                 listWidth : 100,
29033                 triggerAction : 'all',
29034                 typeAhead : true,
29035                 valueField : 'val',
29036                 width : 100,
29037                 name : 'width',
29038                 listeners : {
29039                     select : function (combo, r, index)
29040                     {
29041                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29042                         var b = block();
29043                         b.width = r.get('val');
29044                         b.updateElement();
29045                         syncValue();
29046                         toolbar.editorcore.onEditorEvent();
29047                     }
29048                 },
29049                 xns : rooui.form,
29050                 store : {
29051                     xtype : 'SimpleStore',
29052                     data : [
29053                         ['100%'],
29054                         ['auto']
29055                     ],
29056                     fields : [ 'val'],
29057                     xns : Roo.data
29058                 }
29059             },
29060             // -------- Cols
29061             
29062             {
29063                 xtype : 'TextItem',
29064                 text : "Columns: ",
29065                 xns : rooui.Toolbar  //Boostrap?
29066             },
29067          
29068             {
29069                 xtype : 'Button',
29070                 text: '-',
29071                 listeners : {
29072                     click : function (_self, e)
29073                     {
29074                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29075                         block().removeColumn();
29076                         syncValue();
29077                         toolbar.editorcore.onEditorEvent();
29078                     }
29079                 },
29080                 xns : rooui.Toolbar
29081             },
29082             {
29083                 xtype : 'Button',
29084                 text: '+',
29085                 listeners : {
29086                     click : function (_self, e)
29087                     {
29088                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29089                         block().addColumn();
29090                         syncValue();
29091                         toolbar.editorcore.onEditorEvent();
29092                     }
29093                 },
29094                 xns : rooui.Toolbar
29095             },
29096             // -------- ROWS
29097             {
29098                 xtype : 'TextItem',
29099                 text : "Rows: ",
29100                 xns : rooui.Toolbar  //Boostrap?
29101             },
29102          
29103             {
29104                 xtype : 'Button',
29105                 text: '-',
29106                 listeners : {
29107                     click : function (_self, e)
29108                     {
29109                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29110                         block().removeRow();
29111                         syncValue();
29112                         toolbar.editorcore.onEditorEvent();
29113                     }
29114                 },
29115                 xns : rooui.Toolbar
29116             },
29117             {
29118                 xtype : 'Button',
29119                 text: '+',
29120                 listeners : {
29121                     click : function (_self, e)
29122                     {
29123                         block().addRow();
29124                         syncValue();
29125                         toolbar.editorcore.onEditorEvent();
29126                     }
29127                 },
29128                 xns : rooui.Toolbar
29129             },
29130             // -------- ROWS
29131             {
29132                 xtype : 'Button',
29133                 text: 'Reset Column Widths',
29134                 listeners : {
29135                     
29136                     click : function (_self, e)
29137                     {
29138                         block().resetWidths();
29139                         syncValue();
29140                         toolbar.editorcore.onEditorEvent();
29141                     }
29142                 },
29143                 xns : rooui.Toolbar
29144             } 
29145             
29146             
29147             
29148         ];
29149         
29150     },
29151     
29152     
29153   /**
29154      * create a DomHelper friendly object - for use with
29155      * Roo.DomHelper.markup / overwrite / etc..
29156      * ?? should it be called with option to hide all editing features?
29157      */
29158     toObject : function()
29159     {
29160         
29161         var ret = {
29162             tag : 'table',
29163             contenteditable : 'false', // this stops cell selection from picking the table.
29164             'data-block' : 'Table',
29165             style : {
29166                 width:  this.width,
29167                 border : 'solid 1px #000', // ??? hard coded?
29168                 'border-collapse' : 'collapse' 
29169             },
29170             cn : [
29171                 { tag : 'tbody' , cn : [] }
29172             ]
29173         };
29174         
29175         // do we have a head = not really 
29176         var ncols = 0;
29177         Roo.each(this.rows, function( row ) {
29178             var tr = {
29179                 tag: 'tr',
29180                 style : {
29181                     margin: '6px',
29182                     border : 'solid 1px #000',
29183                     textAlign : 'left' 
29184                 },
29185                 cn : [ ]
29186             };
29187             
29188             ret.cn[0].cn.push(tr);
29189             // does the row have any properties? ?? height?
29190             var nc = 0;
29191             Roo.each(row, function( cell ) {
29192                 
29193                 var td = {
29194                     tag : 'td',
29195                     contenteditable :  'true',
29196                     'data-block' : 'Td',
29197                     html : cell.html,
29198                     style : cell.style
29199                 };
29200                 if (cell.colspan > 1) {
29201                     td.colspan = cell.colspan ;
29202                     nc += cell.colspan;
29203                 } else {
29204                     nc++;
29205                 }
29206                 if (cell.rowspan > 1) {
29207                     td.rowspan = cell.rowspan ;
29208                 }
29209                 
29210                 
29211                 // widths ?
29212                 tr.cn.push(td);
29213                     
29214                 
29215             }, this);
29216             ncols = Math.max(nc, ncols);
29217             
29218             
29219         }, this);
29220         // add the header row..
29221         
29222         ncols++;
29223          
29224         
29225         return ret;
29226          
29227     },
29228     
29229     readElement : function(node)
29230     {
29231         node  = node ? node : this.node ;
29232         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29233         
29234         this.rows = [];
29235         this.no_row = 0;
29236         var trs = Array.from(node.rows);
29237         trs.forEach(function(tr) {
29238             var row =  [];
29239             this.rows.push(row);
29240             
29241             this.no_row++;
29242             var no_column = 0;
29243             Array.from(tr.cells).forEach(function(td) {
29244                 
29245                 var add = {
29246                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29247                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29248                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29249                     html : td.innerHTML
29250                 };
29251                 no_column += add.colspan;
29252                      
29253                 
29254                 row.push(add);
29255                 
29256                 
29257             },this);
29258             this.no_col = Math.max(this.no_col, no_column);
29259             
29260             
29261         },this);
29262         
29263         
29264     },
29265     normalizeRows: function()
29266     {
29267         var ret= [];
29268         var rid = -1;
29269         this.rows.forEach(function(row) {
29270             rid++;
29271             ret[rid] = [];
29272             row = this.normalizeRow(row);
29273             var cid = 0;
29274             row.forEach(function(c) {
29275                 while (typeof(ret[rid][cid]) != 'undefined') {
29276                     cid++;
29277                 }
29278                 if (typeof(ret[rid]) == 'undefined') {
29279                     ret[rid] = [];
29280                 }
29281                 ret[rid][cid] = c;
29282                 c.row = rid;
29283                 c.col = cid;
29284                 if (c.rowspan < 2) {
29285                     return;
29286                 }
29287                 
29288                 for(var i = 1 ;i < c.rowspan; i++) {
29289                     if (typeof(ret[rid+i]) == 'undefined') {
29290                         ret[rid+i] = [];
29291                     }
29292                     ret[rid+i][cid] = c;
29293                 }
29294             });
29295         }, this);
29296         return ret;
29297     
29298     },
29299     
29300     normalizeRow: function(row)
29301     {
29302         var ret= [];
29303         row.forEach(function(c) {
29304             if (c.colspan < 2) {
29305                 ret.push(c);
29306                 return;
29307             }
29308             for(var i =0 ;i < c.colspan; i++) {
29309                 ret.push(c);
29310             }
29311         });
29312         return ret;
29313     
29314     },
29315     
29316     deleteColumn : function(sel)
29317     {
29318         if (!sel || sel.type != 'col') {
29319             return;
29320         }
29321         if (this.no_col < 2) {
29322             return;
29323         }
29324         
29325         this.rows.forEach(function(row) {
29326             var cols = this.normalizeRow(row);
29327             var col = cols[sel.col];
29328             if (col.colspan > 1) {
29329                 col.colspan --;
29330             } else {
29331                 row.remove(col);
29332             }
29333             
29334         }, this);
29335         this.no_col--;
29336         
29337     },
29338     removeColumn : function()
29339     {
29340         this.deleteColumn({
29341             type: 'col',
29342             col : this.no_col-1
29343         });
29344         this.updateElement();
29345     },
29346     
29347      
29348     addColumn : function()
29349     {
29350         
29351         this.rows.forEach(function(row) {
29352             row.push(this.emptyCell());
29353            
29354         }, this);
29355         this.updateElement();
29356     },
29357     
29358     deleteRow : function(sel)
29359     {
29360         if (!sel || sel.type != 'row') {
29361             return;
29362         }
29363         
29364         if (this.no_row < 2) {
29365             return;
29366         }
29367         
29368         var rows = this.normalizeRows();
29369         
29370         
29371         rows[sel.row].forEach(function(col) {
29372             if (col.rowspan > 1) {
29373                 col.rowspan--;
29374             } else {
29375                 col.remove = 1; // flage it as removed.
29376             }
29377             
29378         }, this);
29379         var newrows = [];
29380         this.rows.forEach(function(row) {
29381             newrow = [];
29382             row.forEach(function(c) {
29383                 if (typeof(c.remove) == 'undefined') {
29384                     newrow.push(c);
29385                 }
29386                 
29387             });
29388             if (newrow.length > 0) {
29389                 newrows.push(row);
29390             }
29391         });
29392         this.rows =  newrows;
29393         
29394         
29395         
29396         this.no_row--;
29397         this.updateElement();
29398         
29399     },
29400     removeRow : function()
29401     {
29402         this.deleteRow({
29403             type: 'row',
29404             row : this.no_row-1
29405         });
29406         
29407     },
29408     
29409      
29410     addRow : function()
29411     {
29412         
29413         var row = [];
29414         for (var i = 0; i < this.no_col; i++ ) {
29415             
29416             row.push(this.emptyCell());
29417            
29418         }
29419         this.rows.push(row);
29420         this.updateElement();
29421         
29422     },
29423      
29424     // the default cell object... at present...
29425     emptyCell : function() {
29426         return (new Roo.htmleditor.BlockTd({})).toObject();
29427         
29428      
29429     },
29430     
29431     removeNode : function()
29432     {
29433         return this.node;
29434     },
29435     
29436     
29437     
29438     resetWidths : function()
29439     {
29440         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29441             var nn = Roo.htmleditor.Block.factory(n);
29442             nn.width = '';
29443             nn.updateElement(n);
29444         });
29445     }
29446     
29447     
29448     
29449     
29450 })
29451
29452 /**
29453  *
29454  * editing a TD?
29455  *
29456  * since selections really work on the table cell, then editing really should work from there
29457  *
29458  * The original plan was to support merging etc... - but that may not be needed yet..
29459  *
29460  * So this simple version will support:
29461  *   add/remove cols
29462  *   adjust the width +/-
29463  *   reset the width...
29464  *   
29465  *
29466  */
29467
29468
29469  
29470
29471 /**
29472  * @class Roo.htmleditor.BlockTable
29473  * Block that manages a table
29474  * 
29475  * @constructor
29476  * Create a new Filter.
29477  * @param {Object} config Configuration options
29478  */
29479
29480 Roo.htmleditor.BlockTd = function(cfg)
29481 {
29482     if (cfg.node) {
29483         this.readElement(cfg.node);
29484         this.updateElement(cfg.node);
29485     }
29486     Roo.apply(this, cfg);
29487      
29488     
29489     
29490 }
29491 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29492  
29493     node : false,
29494     
29495     width: '',
29496     textAlign : 'left',
29497     valign : 'top',
29498     
29499     colspan : 1,
29500     rowspan : 1,
29501     
29502     
29503     // used by context menu
29504     friendly_name : 'Table Cell',
29505     deleteTitle : false, // use our customer delete
29506     
29507     // context menu is drawn once..
29508     
29509     contextMenu : function(toolbar)
29510     {
29511         
29512         var cell = function() {
29513             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29514         };
29515         
29516         var table = function() {
29517             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
29518         };
29519         
29520         var lr = false;
29521         var saveSel = function()
29522         {
29523             lr = toolbar.editorcore.getSelection().getRangeAt(0);
29524         }
29525         var restoreSel = function()
29526         {
29527             if (lr) {
29528                 (function() {
29529                     toolbar.editorcore.focus();
29530                     var cr = toolbar.editorcore.getSelection();
29531                     cr.removeAllRanges();
29532                     cr.addRange(lr);
29533                     toolbar.editorcore.onEditorEvent();
29534                 }).defer(10, this);
29535                 
29536                 
29537             }
29538         }
29539         
29540         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29541         
29542         var syncValue = toolbar.editorcore.syncValue;
29543         
29544         var fields = {};
29545         
29546         return [
29547             {
29548                 xtype : 'Button',
29549                 text : 'Edit Table',
29550                 listeners : {
29551                     click : function() {
29552                         var t = toolbar.tb.selectedNode.closest('table');
29553                         toolbar.editorcore.selectNode(t);
29554                         toolbar.editorcore.onEditorEvent();                        
29555                     }
29556                 }
29557                 
29558             },
29559               
29560            
29561              
29562             {
29563                 xtype : 'TextItem',
29564                 text : "Column Width: ",
29565                  xns : rooui.Toolbar 
29566                
29567             },
29568             {
29569                 xtype : 'Button',
29570                 text: '-',
29571                 listeners : {
29572                     click : function (_self, e)
29573                     {
29574                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29575                         cell().shrinkColumn();
29576                         syncValue();
29577                          toolbar.editorcore.onEditorEvent();
29578                     }
29579                 },
29580                 xns : rooui.Toolbar
29581             },
29582             {
29583                 xtype : 'Button',
29584                 text: '+',
29585                 listeners : {
29586                     click : function (_self, e)
29587                     {
29588                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29589                         cell().growColumn();
29590                         syncValue();
29591                         toolbar.editorcore.onEditorEvent();
29592                     }
29593                 },
29594                 xns : rooui.Toolbar
29595             },
29596             
29597             {
29598                 xtype : 'TextItem',
29599                 text : "Vertical Align: ",
29600                 xns : rooui.Toolbar  //Boostrap?
29601             },
29602             {
29603                 xtype : 'ComboBox',
29604                 allowBlank : false,
29605                 displayField : 'val',
29606                 editable : true,
29607                 listWidth : 100,
29608                 triggerAction : 'all',
29609                 typeAhead : true,
29610                 valueField : 'val',
29611                 width : 100,
29612                 name : 'valign',
29613                 listeners : {
29614                     select : function (combo, r, index)
29615                     {
29616                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29617                         var b = cell();
29618                         b.valign = r.get('val');
29619                         b.updateElement();
29620                         syncValue();
29621                         toolbar.editorcore.onEditorEvent();
29622                     }
29623                 },
29624                 xns : rooui.form,
29625                 store : {
29626                     xtype : 'SimpleStore',
29627                     data : [
29628                         ['top'],
29629                         ['middle'],
29630                         ['bottom'] // there are afew more... 
29631                     ],
29632                     fields : [ 'val'],
29633                     xns : Roo.data
29634                 }
29635             },
29636             
29637             {
29638                 xtype : 'TextItem',
29639                 text : "Merge Cells: ",
29640                  xns : rooui.Toolbar 
29641                
29642             },
29643             
29644             
29645             {
29646                 xtype : 'Button',
29647                 text: 'Right',
29648                 listeners : {
29649                     click : function (_self, e)
29650                     {
29651                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29652                         cell().mergeRight();
29653                         //block().growColumn();
29654                         syncValue();
29655                         toolbar.editorcore.onEditorEvent();
29656                     }
29657                 },
29658                 xns : rooui.Toolbar
29659             },
29660              
29661             {
29662                 xtype : 'Button',
29663                 text: 'Below',
29664                 listeners : {
29665                     click : function (_self, e)
29666                     {
29667                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29668                         cell().mergeBelow();
29669                         //block().growColumn();
29670                         syncValue();
29671                         toolbar.editorcore.onEditorEvent();
29672                     }
29673                 },
29674                 xns : rooui.Toolbar
29675             },
29676             {
29677                 xtype : 'TextItem',
29678                 text : "| ",
29679                  xns : rooui.Toolbar 
29680                
29681             },
29682             
29683             {
29684                 xtype : 'Button',
29685                 text: 'Split',
29686                 listeners : {
29687                     click : function (_self, e)
29688                     {
29689                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29690                         cell().split();
29691                         syncValue();
29692                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29693                         toolbar.editorcore.onEditorEvent();
29694                                              
29695                     }
29696                 },
29697                 xns : rooui.Toolbar
29698             },
29699             {
29700                 xtype : 'Fill',
29701                 xns : rooui.Toolbar 
29702                
29703             },
29704         
29705           
29706             {
29707                 xtype : 'Button',
29708                 text: 'Delete',
29709                  
29710                 xns : rooui.Toolbar,
29711                 menu : {
29712                     xtype : 'Menu',
29713                     xns : rooui.menu,
29714                     items : [
29715                         {
29716                             xtype : 'Item',
29717                             html: 'Column',
29718                             listeners : {
29719                                 click : function (_self, e)
29720                                 {
29721                                     var t = table();
29722                                     
29723                                     cell().deleteColumn();
29724                                     syncValue();
29725                                     toolbar.editorcore.selectNode(t.node);
29726                                     toolbar.editorcore.onEditorEvent();   
29727                                 }
29728                             },
29729                             xns : rooui.menu
29730                         },
29731                         {
29732                             xtype : 'Item',
29733                             html: 'Row',
29734                             listeners : {
29735                                 click : function (_self, e)
29736                                 {
29737                                     var t = table();
29738                                     cell().deleteRow();
29739                                     syncValue();
29740                                     
29741                                     toolbar.editorcore.selectNode(t.node);
29742                                     toolbar.editorcore.onEditorEvent();   
29743                                                          
29744                                 }
29745                             },
29746                             xns : rooui.menu
29747                         },
29748                        {
29749                             xtype : 'Separator',
29750                             xns : rooui.menu
29751                         },
29752                         {
29753                             xtype : 'Item',
29754                             html: 'Table',
29755                             listeners : {
29756                                 click : function (_self, e)
29757                                 {
29758                                     var t = table();
29759                                     var nn = t.node.nextSibling || t.node.previousSibling;
29760                                     t.node.parentNode.removeChild(t.node);
29761                                     if (nn) { 
29762                                         toolbar.editorcore.selectNode(nn, true);
29763                                     }
29764                                     toolbar.editorcore.onEditorEvent();   
29765                                                          
29766                                 }
29767                             },
29768                             xns : rooui.menu
29769                         }
29770                     ]
29771                 }
29772             }
29773             
29774             // align... << fixme
29775             
29776         ];
29777         
29778     },
29779     
29780     
29781   /**
29782      * create a DomHelper friendly object - for use with
29783      * Roo.DomHelper.markup / overwrite / etc..
29784      * ?? should it be called with option to hide all editing features?
29785      */
29786  /**
29787      * create a DomHelper friendly object - for use with
29788      * Roo.DomHelper.markup / overwrite / etc..
29789      * ?? should it be called with option to hide all editing features?
29790      */
29791     toObject : function()
29792     {
29793         var ret = {
29794             tag : 'td',
29795             contenteditable : 'true', // this stops cell selection from picking the table.
29796             'data-block' : 'Td',
29797             valign : this.valign,
29798             style : {  
29799                 'text-align' :  this.textAlign,
29800                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
29801                 'border-collapse' : 'collapse',
29802                 padding : '6px', // 8 for desktop / 4 for mobile
29803                 'vertical-align': this.valign
29804             },
29805             html : this.html
29806         };
29807         if (this.width != '') {
29808             ret.width = this.width;
29809             ret.style.width = this.width;
29810         }
29811         
29812         
29813         if (this.colspan > 1) {
29814             ret.colspan = this.colspan ;
29815         } 
29816         if (this.rowspan > 1) {
29817             ret.rowspan = this.rowspan ;
29818         }
29819         
29820            
29821         
29822         return ret;
29823          
29824     },
29825     
29826     readElement : function(node)
29827     {
29828         node  = node ? node : this.node ;
29829         this.width = node.style.width;
29830         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
29831         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
29832         this.html = node.innerHTML;
29833         if (node.style.textAlign != '') {
29834             this.textAlign = node.style.textAlign;
29835         }
29836         
29837         
29838     },
29839      
29840     // the default cell object... at present...
29841     emptyCell : function() {
29842         return {
29843             colspan :  1,
29844             rowspan :  1,
29845             textAlign : 'left',
29846             html : "&nbsp;" // is this going to be editable now?
29847         };
29848      
29849     },
29850     
29851     removeNode : function()
29852     {
29853         return this.node.closest('table');
29854          
29855     },
29856     
29857     cellData : false,
29858     
29859     colWidths : false,
29860     
29861     toTableArray  : function()
29862     {
29863         var ret = [];
29864         var tab = this.node.closest('tr').closest('table');
29865         Array.from(tab.rows).forEach(function(r, ri){
29866             ret[ri] = [];
29867         });
29868         var rn = 0;
29869         this.colWidths = [];
29870         var all_auto = true;
29871         Array.from(tab.rows).forEach(function(r, ri){
29872             
29873             var cn = 0;
29874             Array.from(r.cells).forEach(function(ce, ci){
29875                 var c =  {
29876                     cell : ce,
29877                     row : rn,
29878                     col: cn,
29879                     colspan : ce.colSpan,
29880                     rowspan : ce.rowSpan
29881                 };
29882                 if (ce.isEqualNode(this.node)) {
29883                     this.cellData = c;
29884                 }
29885                 // if we have been filled up by a row?
29886                 if (typeof(ret[rn][cn]) != 'undefined') {
29887                     while(typeof(ret[rn][cn]) != 'undefined') {
29888                         cn++;
29889                     }
29890                     c.col = cn;
29891                 }
29892                 
29893                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
29894                     this.colWidths[cn] =   ce.style.width;
29895                     if (this.colWidths[cn] != '') {
29896                         all_auto = false;
29897                     }
29898                 }
29899                 
29900                 
29901                 if (c.colspan < 2 && c.rowspan < 2 ) {
29902                     ret[rn][cn] = c;
29903                     cn++;
29904                     return;
29905                 }
29906                 for(var j = 0; j < c.rowspan; j++) {
29907                     if (typeof(ret[rn+j]) == 'undefined') {
29908                         continue; // we have a problem..
29909                     }
29910                     ret[rn+j][cn] = c;
29911                     for(var i = 0; i < c.colspan; i++) {
29912                         ret[rn+j][cn+i] = c;
29913                     }
29914                 }
29915                 
29916                 cn += c.colspan;
29917             }, this);
29918             rn++;
29919         }, this);
29920         
29921         // initalize widths.?
29922         // either all widths or no widths..
29923         if (all_auto) {
29924             this.colWidths[0] = false; // no widths flag.
29925         }
29926         
29927         
29928         return ret;
29929         
29930     },
29931     
29932     
29933     
29934     
29935     mergeRight: function()
29936     {
29937          
29938         // get the contents of the next cell along..
29939         var tr = this.node.closest('tr');
29940         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
29941         if (i >= tr.childNodes.length - 1) {
29942             return; // no cells on right to merge with.
29943         }
29944         var table = this.toTableArray();
29945         
29946         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
29947             return; // nothing right?
29948         }
29949         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
29950         // right cell - must be same rowspan and on the same row.
29951         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
29952             return; // right hand side is not same rowspan.
29953         }
29954         
29955         
29956         
29957         this.node.innerHTML += ' ' + rc.cell.innerHTML;
29958         tr.removeChild(rc.cell);
29959         this.colspan += rc.colspan;
29960         this.node.setAttribute('colspan', this.colspan);
29961
29962         var table = this.toTableArray();
29963         this.normalizeWidths(table);
29964         this.updateWidths(table);
29965     },
29966     
29967     
29968     mergeBelow : function()
29969     {
29970         var table = this.toTableArray();
29971         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
29972             return; // no row below
29973         }
29974         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
29975             return; // nothing right?
29976         }
29977         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
29978         
29979         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
29980             return; // right hand side is not same rowspan.
29981         }
29982         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
29983         rc.cell.parentNode.removeChild(rc.cell);
29984         this.rowspan += rc.rowspan;
29985         this.node.setAttribute('rowspan', this.rowspan);
29986     },
29987     
29988     split: function()
29989     {
29990         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
29991             return;
29992         }
29993         var table = this.toTableArray();
29994         var cd = this.cellData;
29995         this.rowspan = 1;
29996         this.colspan = 1;
29997         
29998         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
29999              
30000             
30001             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30002                 if (r == cd.row && c == cd.col) {
30003                     this.node.removeAttribute('rowspan');
30004                     this.node.removeAttribute('colspan');
30005                 }
30006                  
30007                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30008                 ntd.removeAttribute('id'); 
30009                 ntd.style.width  = this.colWidths[c];
30010                 ntd.innerHTML = '';
30011                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30012             }
30013             
30014         }
30015         this.redrawAllCells(table);
30016         
30017     },
30018     
30019     
30020     
30021     redrawAllCells: function(table)
30022     {
30023         
30024          
30025         var tab = this.node.closest('tr').closest('table');
30026         var ctr = tab.rows[0].parentNode;
30027         Array.from(tab.rows).forEach(function(r, ri){
30028             
30029             Array.from(r.cells).forEach(function(ce, ci){
30030                 ce.parentNode.removeChild(ce);
30031             });
30032             r.parentNode.removeChild(r);
30033         });
30034         for(var r = 0 ; r < table.length; r++) {
30035             var re = tab.rows[r];
30036             
30037             var re = tab.ownerDocument.createElement('tr');
30038             ctr.appendChild(re);
30039             for(var c = 0 ; c < table[r].length; c++) {
30040                 if (table[r][c].cell === false) {
30041                     continue;
30042                 }
30043                 
30044                 re.appendChild(table[r][c].cell);
30045                  
30046                 table[r][c].cell = false;
30047             }
30048         }
30049         
30050     },
30051     updateWidths : function(table)
30052     {
30053         for(var r = 0 ; r < table.length; r++) {
30054            
30055             for(var c = 0 ; c < table[r].length; c++) {
30056                 if (table[r][c].cell === false) {
30057                     continue;
30058                 }
30059                 
30060                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30061                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30062                     el.width = Math.floor(this.colWidths[c])  +'%';
30063                     el.updateElement(el.node);
30064                 }
30065                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30066                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30067                     var width = 0;
30068                     for(var i = 0; i < table[r][c].colspan; i ++) {
30069                         width += Math.floor(this.colWidths[c + i]);
30070                     }
30071                     el.width = width  +'%';
30072                     el.updateElement(el.node);
30073                 }
30074                 table[r][c].cell = false; // done
30075             }
30076         }
30077     },
30078     normalizeWidths : function(table)
30079     {
30080         if (this.colWidths[0] === false) {
30081             var nw = 100.0 / this.colWidths.length;
30082             this.colWidths.forEach(function(w,i) {
30083                 this.colWidths[i] = nw;
30084             },this);
30085             return;
30086         }
30087     
30088         var t = 0, missing = [];
30089         
30090         this.colWidths.forEach(function(w,i) {
30091             //if you mix % and
30092             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30093             var add =  this.colWidths[i];
30094             if (add > 0) {
30095                 t+=add;
30096                 return;
30097             }
30098             missing.push(i);
30099             
30100             
30101         },this);
30102         var nc = this.colWidths.length;
30103         if (missing.length) {
30104             var mult = (nc - missing.length) / (1.0 * nc);
30105             var t = mult * t;
30106             var ew = (100 -t) / (1.0 * missing.length);
30107             this.colWidths.forEach(function(w,i) {
30108                 if (w > 0) {
30109                     this.colWidths[i] = w * mult;
30110                     return;
30111                 }
30112                 
30113                 this.colWidths[i] = ew;
30114             }, this);
30115             // have to make up numbers..
30116              
30117         }
30118         // now we should have all the widths..
30119         
30120     
30121     },
30122     
30123     shrinkColumn : function()
30124     {
30125         var table = this.toTableArray();
30126         this.normalizeWidths(table);
30127         var col = this.cellData.col;
30128         var nw = this.colWidths[col] * 0.8;
30129         if (nw < 5) {
30130             return;
30131         }
30132         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30133         this.colWidths.forEach(function(w,i) {
30134             if (i == col) {
30135                  this.colWidths[i] = nw;
30136                 return;
30137             }
30138             this.colWidths[i] += otherAdd
30139         }, this);
30140         this.updateWidths(table);
30141          
30142     },
30143     growColumn : function()
30144     {
30145         var table = this.toTableArray();
30146         this.normalizeWidths(table);
30147         var col = this.cellData.col;
30148         var nw = this.colWidths[col] * 1.2;
30149         if (nw > 90) {
30150             return;
30151         }
30152         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30153         this.colWidths.forEach(function(w,i) {
30154             if (i == col) {
30155                 this.colWidths[i] = nw;
30156                 return;
30157             }
30158             this.colWidths[i] -= otherSub
30159         }, this);
30160         this.updateWidths(table);
30161          
30162     },
30163     deleteRow : function()
30164     {
30165         // delete this rows 'tr'
30166         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30167         // then reduce the rowspan.
30168         var table = this.toTableArray();
30169         // this.cellData.row;
30170         for (var i =0;i< table[this.cellData.row].length ; i++) {
30171             var c = table[this.cellData.row][i];
30172             if (c.row != this.cellData.row) {
30173                 
30174                 c.rowspan--;
30175                 c.cell.setAttribute('rowspan', c.rowspan);
30176                 continue;
30177             }
30178             if (c.rowspan > 1) {
30179                 c.rowspan--;
30180                 c.cell.setAttribute('rowspan', c.rowspan);
30181             }
30182         }
30183         table.splice(this.cellData.row,1);
30184         this.redrawAllCells(table);
30185         
30186     },
30187     deleteColumn : function()
30188     {
30189         var table = this.toTableArray();
30190         
30191         for (var i =0;i< table.length ; i++) {
30192             var c = table[i][this.cellData.col];
30193             if (c.col != this.cellData.col) {
30194                 table[i][this.cellData.col].colspan--;
30195             } else if (c.colspan > 1) {
30196                 c.colspan--;
30197                 c.cell.setAttribute('colspan', c.colspan);
30198             }
30199             table[i].splice(this.cellData.col,1);
30200         }
30201         
30202         this.redrawAllCells(table);
30203     }
30204     
30205     
30206     
30207     
30208 })
30209
30210 //<script type="text/javascript">
30211
30212 /*
30213  * Based  Ext JS Library 1.1.1
30214  * Copyright(c) 2006-2007, Ext JS, LLC.
30215  * LGPL
30216  *
30217  */
30218  
30219 /**
30220  * @class Roo.HtmlEditorCore
30221  * @extends Roo.Component
30222  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30223  *
30224  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30225  */
30226
30227 Roo.HtmlEditorCore = function(config){
30228     
30229     
30230     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30231     
30232     
30233     this.addEvents({
30234         /**
30235          * @event initialize
30236          * Fires when the editor is fully initialized (including the iframe)
30237          * @param {Roo.HtmlEditorCore} this
30238          */
30239         initialize: true,
30240         /**
30241          * @event activate
30242          * Fires when the editor is first receives the focus. Any insertion must wait
30243          * until after this event.
30244          * @param {Roo.HtmlEditorCore} this
30245          */
30246         activate: true,
30247          /**
30248          * @event beforesync
30249          * Fires before the textarea is updated with content from the editor iframe. Return false
30250          * to cancel the sync.
30251          * @param {Roo.HtmlEditorCore} this
30252          * @param {String} html
30253          */
30254         beforesync: true,
30255          /**
30256          * @event beforepush
30257          * Fires before the iframe editor is updated with content from the textarea. Return false
30258          * to cancel the push.
30259          * @param {Roo.HtmlEditorCore} this
30260          * @param {String} html
30261          */
30262         beforepush: true,
30263          /**
30264          * @event sync
30265          * Fires when the textarea is updated with content from the editor iframe.
30266          * @param {Roo.HtmlEditorCore} this
30267          * @param {String} html
30268          */
30269         sync: true,
30270          /**
30271          * @event push
30272          * Fires when the iframe editor is updated with content from the textarea.
30273          * @param {Roo.HtmlEditorCore} this
30274          * @param {String} html
30275          */
30276         push: true,
30277         
30278         /**
30279          * @event editorevent
30280          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30281          * @param {Roo.HtmlEditorCore} this
30282          */
30283         editorevent: true 
30284          
30285         
30286     });
30287     
30288     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30289     
30290     // defaults : white / black...
30291     this.applyBlacklists();
30292     
30293     
30294     
30295 };
30296
30297
30298 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30299
30300
30301      /**
30302      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30303      */
30304     
30305     owner : false,
30306     
30307      /**
30308      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30309      *                        Roo.resizable.
30310      */
30311     resizable : false,
30312      /**
30313      * @cfg {Number} height (in pixels)
30314      */   
30315     height: 300,
30316    /**
30317      * @cfg {Number} width (in pixels)
30318      */   
30319     width: 500,
30320      /**
30321      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30322      *         if you are doing an email editor, this probably needs disabling, it's designed
30323      */
30324     autoClean: true,
30325     
30326     /**
30327      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30328      */
30329     enableBlocks : true,
30330     /**
30331      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30332      * 
30333      */
30334     stylesheets: false,
30335      /**
30336      * @cfg {String} language default en - language of text (usefull for rtl languages)
30337      * 
30338      */
30339     language: 'en',
30340     
30341     /**
30342      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30343      *          - by default they are stripped - if you are editing email you may need this.
30344      */
30345     allowComments: false,
30346     // id of frame..
30347     frameId: false,
30348     
30349     // private properties
30350     validationEvent : false,
30351     deferHeight: true,
30352     initialized : false,
30353     activated : false,
30354     sourceEditMode : false,
30355     onFocus : Roo.emptyFn,
30356     iframePad:3,
30357     hideMode:'offsets',
30358     
30359     clearUp: true,
30360     
30361     // blacklist + whitelisted elements..
30362     black: false,
30363     white: false,
30364      
30365     bodyCls : '',
30366
30367     
30368     undoManager : false,
30369     /**
30370      * Protected method that will not generally be called directly. It
30371      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30372      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30373      */
30374     getDocMarkup : function(){
30375         // body styles..
30376         var st = '';
30377         
30378         // inherit styels from page...?? 
30379         if (this.stylesheets === false) {
30380             
30381             Roo.get(document.head).select('style').each(function(node) {
30382                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30383             });
30384             
30385             Roo.get(document.head).select('link').each(function(node) { 
30386                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30387             });
30388             
30389         } else if (!this.stylesheets.length) {
30390                 // simple..
30391                 st = '<style type="text/css">' +
30392                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30393                    '</style>';
30394         } else {
30395             for (var i in this.stylesheets) {
30396                 if (typeof(this.stylesheets[i]) != 'string') {
30397                     continue;
30398                 }
30399                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30400             }
30401             
30402         }
30403         
30404         st +=  '<style type="text/css">' +
30405             'IMG { cursor: pointer } ' +
30406         '</style>';
30407         
30408         st += '<meta name="google" content="notranslate">';
30409         
30410         var cls = 'notranslate roo-htmleditor-body';
30411         
30412         if(this.bodyCls.length){
30413             cls += ' ' + this.bodyCls;
30414         }
30415         
30416         return '<html  class="notranslate" translate="no"><head>' + st  +
30417             //<style type="text/css">' +
30418             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30419             //'</style>' +
30420             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30421     },
30422
30423     // private
30424     onRender : function(ct, position)
30425     {
30426         var _t = this;
30427         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30428         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30429         
30430         
30431         this.el.dom.style.border = '0 none';
30432         this.el.dom.setAttribute('tabIndex', -1);
30433         this.el.addClass('x-hidden hide');
30434         
30435         
30436         
30437         if(Roo.isIE){ // fix IE 1px bogus margin
30438             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30439         }
30440        
30441         
30442         this.frameId = Roo.id();
30443         
30444          
30445         
30446         var iframe = this.owner.wrap.createChild({
30447             tag: 'iframe',
30448             cls: 'form-control', // bootstrap..
30449             id: this.frameId,
30450             name: this.frameId,
30451             frameBorder : 'no',
30452             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
30453         }, this.el
30454         );
30455         
30456         
30457         this.iframe = iframe.dom;
30458
30459         this.assignDocWin();
30460         
30461         this.doc.designMode = 'on';
30462        
30463         this.doc.open();
30464         this.doc.write(this.getDocMarkup());
30465         this.doc.close();
30466
30467         
30468         var task = { // must defer to wait for browser to be ready
30469             run : function(){
30470                 //console.log("run task?" + this.doc.readyState);
30471                 this.assignDocWin();
30472                 if(this.doc.body || this.doc.readyState == 'complete'){
30473                     try {
30474                         this.doc.designMode="on";
30475                         
30476                     } catch (e) {
30477                         return;
30478                     }
30479                     Roo.TaskMgr.stop(task);
30480                     this.initEditor.defer(10, this);
30481                 }
30482             },
30483             interval : 10,
30484             duration: 10000,
30485             scope: this
30486         };
30487         Roo.TaskMgr.start(task);
30488
30489     },
30490
30491     // private
30492     onResize : function(w, h)
30493     {
30494          Roo.log('resize: ' +w + ',' + h );
30495         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30496         if(!this.iframe){
30497             return;
30498         }
30499         if(typeof w == 'number'){
30500             
30501             this.iframe.style.width = w + 'px';
30502         }
30503         if(typeof h == 'number'){
30504             
30505             this.iframe.style.height = h + 'px';
30506             if(this.doc){
30507                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
30508             }
30509         }
30510         
30511     },
30512
30513     /**
30514      * Toggles the editor between standard and source edit mode.
30515      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30516      */
30517     toggleSourceEdit : function(sourceEditMode){
30518         
30519         this.sourceEditMode = sourceEditMode === true;
30520         
30521         if(this.sourceEditMode){
30522  
30523             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
30524             
30525         }else{
30526             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
30527             //this.iframe.className = '';
30528             this.deferFocus();
30529         }
30530         //this.setSize(this.owner.wrap.getSize());
30531         //this.fireEvent('editmodechange', this, this.sourceEditMode);
30532     },
30533
30534     
30535   
30536
30537     /**
30538      * Protected method that will not generally be called directly. If you need/want
30539      * custom HTML cleanup, this is the method you should override.
30540      * @param {String} html The HTML to be cleaned
30541      * return {String} The cleaned HTML
30542      */
30543     cleanHtml : function(html)
30544     {
30545         html = String(html);
30546         if(html.length > 5){
30547             if(Roo.isSafari){ // strip safari nonsense
30548                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
30549             }
30550         }
30551         if(html == '&nbsp;'){
30552             html = '';
30553         }
30554         return html;
30555     },
30556
30557     /**
30558      * HTML Editor -> Textarea
30559      * Protected method that will not generally be called directly. Syncs the contents
30560      * of the editor iframe with the textarea.
30561      */
30562     syncValue : function()
30563     {
30564         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
30565         if(this.initialized){
30566             
30567             if (this.undoManager) {
30568                 this.undoManager.addEvent();
30569             }
30570
30571             
30572             var bd = (this.doc.body || this.doc.documentElement);
30573            
30574             
30575             var sel = this.win.getSelection();
30576             
30577             var div = document.createElement('div');
30578             div.innerHTML = bd.innerHTML;
30579             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
30580             if (gtx.length > 0) {
30581                 var rm = gtx.item(0).parentNode;
30582                 rm.parentNode.removeChild(rm);
30583             }
30584             
30585            
30586             if (this.enableBlocks) {
30587                 new Roo.htmleditor.FilterBlock({ node : div });
30588             }
30589             
30590             var html = div.innerHTML;
30591             
30592             //?? tidy?
30593             if (this.autoClean) {
30594                 
30595                 new Roo.htmleditor.FilterAttributes({
30596                     node : div,
30597                     attrib_white : [
30598                             'href',
30599                             'src',
30600                             'name',
30601                             'align',
30602                             'colspan',
30603                             'rowspan',
30604                             'data-display',
30605                             'data-width',
30606                             'start' ,
30607                             'style',
30608                             // youtube embed.
30609                             'class',
30610                             'allowfullscreen',
30611                             'frameborder',
30612                             'width',
30613                             'height',
30614                             'alt'
30615                             ],
30616                     attrib_clean : ['href', 'src' ] 
30617                 });
30618                 
30619                 var tidy = new Roo.htmleditor.TidySerializer({
30620                     inner:  true
30621                 });
30622                 html  = tidy.serialize(div);
30623                 
30624             }
30625             
30626             
30627             if(Roo.isSafari){
30628                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
30629                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
30630                 if(m && m[1]){
30631                     html = '<div style="'+m[0]+'">' + html + '</div>';
30632                 }
30633             }
30634             html = this.cleanHtml(html);
30635             // fix up the special chars.. normaly like back quotes in word...
30636             // however we do not want to do this with chinese..
30637             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
30638                 
30639                 var cc = match.charCodeAt();
30640
30641                 // Get the character value, handling surrogate pairs
30642                 if (match.length == 2) {
30643                     // It's a surrogate pair, calculate the Unicode code point
30644                     var high = match.charCodeAt(0) - 0xD800;
30645                     var low  = match.charCodeAt(1) - 0xDC00;
30646                     cc = (high * 0x400) + low + 0x10000;
30647                 }  else if (
30648                     (cc >= 0x4E00 && cc < 0xA000 ) ||
30649                     (cc >= 0x3400 && cc < 0x4E00 ) ||
30650                     (cc >= 0xf900 && cc < 0xfb00 )
30651                 ) {
30652                         return match;
30653                 }  
30654          
30655                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
30656                 return "&#" + cc + ";";
30657                 
30658                 
30659             });
30660             
30661             
30662              
30663             if(this.owner.fireEvent('beforesync', this, html) !== false){
30664                 this.el.dom.value = html;
30665                 this.owner.fireEvent('sync', this, html);
30666             }
30667         }
30668     },
30669
30670     /**
30671      * TEXTAREA -> EDITABLE
30672      * Protected method that will not generally be called directly. Pushes the value of the textarea
30673      * into the iframe editor.
30674      */
30675     pushValue : function()
30676     {
30677         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
30678         if(this.initialized){
30679             var v = this.el.dom.value.trim();
30680             
30681             
30682             if(this.owner.fireEvent('beforepush', this, v) !== false){
30683                 var d = (this.doc.body || this.doc.documentElement);
30684                 d.innerHTML = v;
30685                  
30686                 this.el.dom.value = d.innerHTML;
30687                 this.owner.fireEvent('push', this, v);
30688             }
30689             if (this.autoClean) {
30690                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
30691                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
30692             }
30693             if (this.enableBlocks) {
30694                 Roo.htmleditor.Block.initAll(this.doc.body);
30695             }
30696             
30697             this.updateLanguage();
30698             
30699             var lc = this.doc.body.lastChild;
30700             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
30701                 // add an extra line at the end.
30702                 this.doc.body.appendChild(this.doc.createElement('br'));
30703             }
30704             
30705             
30706         }
30707     },
30708
30709     // private
30710     deferFocus : function(){
30711         this.focus.defer(10, this);
30712     },
30713
30714     // doc'ed in Field
30715     focus : function(){
30716         if(this.win && !this.sourceEditMode){
30717             this.win.focus();
30718         }else{
30719             this.el.focus();
30720         }
30721     },
30722     
30723     assignDocWin: function()
30724     {
30725         var iframe = this.iframe;
30726         
30727          if(Roo.isIE){
30728             this.doc = iframe.contentWindow.document;
30729             this.win = iframe.contentWindow;
30730         } else {
30731 //            if (!Roo.get(this.frameId)) {
30732 //                return;
30733 //            }
30734 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30735 //            this.win = Roo.get(this.frameId).dom.contentWindow;
30736             
30737             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
30738                 return;
30739             }
30740             
30741             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30742             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
30743         }
30744     },
30745     
30746     // private
30747     initEditor : function(){
30748         //console.log("INIT EDITOR");
30749         this.assignDocWin();
30750         
30751         
30752         
30753         this.doc.designMode="on";
30754         this.doc.open();
30755         this.doc.write(this.getDocMarkup());
30756         this.doc.close();
30757         
30758         var dbody = (this.doc.body || this.doc.documentElement);
30759         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
30760         // this copies styles from the containing element into thsi one..
30761         // not sure why we need all of this..
30762         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
30763         
30764         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
30765         //ss['background-attachment'] = 'fixed'; // w3c
30766         dbody.bgProperties = 'fixed'; // ie
30767         dbody.setAttribute("translate", "no");
30768         
30769         //Roo.DomHelper.applyStyles(dbody, ss);
30770         Roo.EventManager.on(this.doc, {
30771              
30772             'mouseup': this.onEditorEvent,
30773             'dblclick': this.onEditorEvent,
30774             'click': this.onEditorEvent,
30775             'keyup': this.onEditorEvent,
30776             
30777             buffer:100,
30778             scope: this
30779         });
30780         Roo.EventManager.on(this.doc, {
30781             'paste': this.onPasteEvent,
30782             scope : this
30783         });
30784         if(Roo.isGecko){
30785             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
30786         }
30787         //??? needed???
30788         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
30789             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
30790         }
30791         this.initialized = true;
30792
30793         
30794         // initialize special key events - enter
30795         new Roo.htmleditor.KeyEnter({core : this});
30796         
30797          
30798         
30799         this.owner.fireEvent('initialize', this);
30800         this.pushValue();
30801     },
30802     // this is to prevent a href clicks resulting in a redirect?
30803    
30804     onPasteEvent : function(e,v)
30805     {
30806         // I think we better assume paste is going to be a dirty load of rubish from word..
30807         
30808         // even pasting into a 'email version' of this widget will have to clean up that mess.
30809         var cd = (e.browserEvent.clipboardData || window.clipboardData);
30810         
30811         // check what type of paste - if it's an image, then handle it differently.
30812         if (cd.files && cd.files.length > 0) {
30813             // pasting images?
30814             var urlAPI = (window.createObjectURL && window) || 
30815                 (window.URL && URL.revokeObjectURL && URL) || 
30816                 (window.webkitURL && webkitURL);
30817     
30818             var url = urlAPI.createObjectURL( cd.files[0]);
30819             this.insertAtCursor('<img src=" + url + ">');
30820             return false;
30821         }
30822         if (cd.types.indexOf('text/html') < 0 ) {
30823             return false;
30824         }
30825         var images = [];
30826         var html = cd.getData('text/html'); // clipboard event
30827         if (cd.types.indexOf('text/rtf') > -1) {
30828             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
30829             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
30830         }
30831         //Roo.log(images);
30832         //Roo.log(imgs);
30833         // fixme..
30834         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
30835                        .map(function(g) { return g.toDataURL(); })
30836                        .filter(function(g) { return g != 'about:blank'; });
30837         
30838         //Roo.log(html);
30839         html = this.cleanWordChars(html);
30840         
30841         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
30842         
30843         
30844         var sn = this.getParentElement();
30845         // check if d contains a table, and prevent nesting??
30846         //Roo.log(d.getElementsByTagName('table'));
30847         //Roo.log(sn);
30848         //Roo.log(sn.closest('table'));
30849         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
30850             e.preventDefault();
30851             this.insertAtCursor("You can not nest tables");
30852             //Roo.log("prevent?"); // fixme - 
30853             return false;
30854         }
30855         
30856         
30857         
30858         if (images.length > 0) {
30859             // replace all v:imagedata - with img.
30860             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
30861             Roo.each(ar, function(node) {
30862                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
30863                 node.parentNode.removeChild(node);
30864             });
30865             
30866             
30867             Roo.each(d.getElementsByTagName('img'), function(img, i) {
30868                 img.setAttribute('src', images[i]);
30869             });
30870         }
30871         if (this.autoClean) {
30872             new Roo.htmleditor.FilterWord({ node : d });
30873             
30874             new Roo.htmleditor.FilterStyleToTag({ node : d });
30875             new Roo.htmleditor.FilterAttributes({
30876                 node : d,
30877                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30878                 attrib_clean : ['href', 'src' ] 
30879             });
30880             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
30881             // should be fonts..
30882             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
30883             new Roo.htmleditor.FilterParagraph({ node : d });
30884             new Roo.htmleditor.FilterSpan({ node : d });
30885             new Roo.htmleditor.FilterLongBr({ node : d });
30886             new Roo.htmleditor.FilterComment({ node : d });
30887             
30888             
30889         }
30890         if (this.enableBlocks) {
30891                 
30892             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
30893                 if (img.closest('figure')) { // assume!! that it's aready
30894                     return;
30895                 }
30896                 var fig  = new Roo.htmleditor.BlockFigure({
30897                     image_src  : img.src
30898                 });
30899                 fig.updateElement(img); // replace it..
30900                 
30901             });
30902         }
30903         
30904         
30905         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
30906         if (this.enableBlocks) {
30907             Roo.htmleditor.Block.initAll(this.doc.body);
30908         }
30909          
30910         
30911         e.preventDefault();
30912         return false;
30913         // default behaveiour should be our local cleanup paste? (optional?)
30914         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
30915         //this.owner.fireEvent('paste', e, v);
30916     },
30917     // private
30918     onDestroy : function(){
30919         
30920         
30921         
30922         if(this.rendered){
30923             
30924             //for (var i =0; i < this.toolbars.length;i++) {
30925             //    // fixme - ask toolbars for heights?
30926             //    this.toolbars[i].onDestroy();
30927            // }
30928             
30929             //this.wrap.dom.innerHTML = '';
30930             //this.wrap.remove();
30931         }
30932     },
30933
30934     // private
30935     onFirstFocus : function(){
30936         
30937         this.assignDocWin();
30938         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
30939         
30940         this.activated = true;
30941          
30942     
30943         if(Roo.isGecko){ // prevent silly gecko errors
30944             this.win.focus();
30945             var s = this.win.getSelection();
30946             if(!s.focusNode || s.focusNode.nodeType != 3){
30947                 var r = s.getRangeAt(0);
30948                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
30949                 r.collapse(true);
30950                 this.deferFocus();
30951             }
30952             try{
30953                 this.execCmd('useCSS', true);
30954                 this.execCmd('styleWithCSS', false);
30955             }catch(e){}
30956         }
30957         this.owner.fireEvent('activate', this);
30958     },
30959
30960     // private
30961     adjustFont: function(btn){
30962         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
30963         //if(Roo.isSafari){ // safari
30964         //    adjust *= 2;
30965        // }
30966         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
30967         if(Roo.isSafari){ // safari
30968             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
30969             v =  (v < 10) ? 10 : v;
30970             v =  (v > 48) ? 48 : v;
30971             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
30972             
30973         }
30974         
30975         
30976         v = Math.max(1, v+adjust);
30977         
30978         this.execCmd('FontSize', v  );
30979     },
30980
30981     onEditorEvent : function(e)
30982     {
30983          
30984         
30985         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
30986             return; // we do not handle this.. (undo manager does..)
30987         }
30988         // in theory this detects if the last element is not a br, then we try and do that.
30989         // its so clicking in space at bottom triggers adding a br and moving the cursor.
30990         if (e &&
30991             e.target.nodeName == 'BODY' &&
30992             e.type == "mouseup" &&
30993             this.doc.body.lastChild
30994            ) {
30995             var lc = this.doc.body.lastChild;
30996             // gtx-trans is google translate plugin adding crap.
30997             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
30998                 lc = lc.previousSibling;
30999             }
31000             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31001             // if last element is <BR> - then dont do anything.
31002             
31003                 var ns = this.doc.createElement('br');
31004                 this.doc.body.appendChild(ns);
31005                 range = this.doc.createRange();
31006                 range.setStartAfter(ns);
31007                 range.collapse(true);
31008                 var sel = this.win.getSelection();
31009                 sel.removeAllRanges();
31010                 sel.addRange(range);
31011             }
31012         }
31013         
31014         
31015         
31016         this.fireEditorEvent(e);
31017       //  this.updateToolbar();
31018         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31019     },
31020     
31021     fireEditorEvent: function(e)
31022     {
31023         this.owner.fireEvent('editorevent', this, e);
31024     },
31025
31026     insertTag : function(tg)
31027     {
31028         // could be a bit smarter... -> wrap the current selected tRoo..
31029         if (tg.toLowerCase() == 'span' ||
31030             tg.toLowerCase() == 'code' ||
31031             tg.toLowerCase() == 'sup' ||
31032             tg.toLowerCase() == 'sub' 
31033             ) {
31034             
31035             range = this.createRange(this.getSelection());
31036             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31037             wrappingNode.appendChild(range.extractContents());
31038             range.insertNode(wrappingNode);
31039
31040             return;
31041             
31042             
31043             
31044         }
31045         this.execCmd("formatblock",   tg);
31046         this.undoManager.addEvent(); 
31047     },
31048     
31049     insertText : function(txt)
31050     {
31051         
31052         
31053         var range = this.createRange();
31054         range.deleteContents();
31055                //alert(Sender.getAttribute('label'));
31056                
31057         range.insertNode(this.doc.createTextNode(txt));
31058         this.undoManager.addEvent();
31059     } ,
31060     
31061      
31062
31063     /**
31064      * Executes a Midas editor command on the editor document and performs necessary focus and
31065      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31066      * @param {String} cmd The Midas command
31067      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31068      */
31069     relayCmd : function(cmd, value)
31070     {
31071         
31072         switch (cmd) {
31073             case 'justifyleft':
31074             case 'justifyright':
31075             case 'justifycenter':
31076                 // if we are in a cell, then we will adjust the
31077                 var n = this.getParentElement();
31078                 var td = n.closest('td');
31079                 if (td) {
31080                     var bl = Roo.htmleditor.Block.factory(td);
31081                     bl.textAlign = cmd.replace('justify','');
31082                     bl.updateElement();
31083                     this.owner.fireEvent('editorevent', this);
31084                     return;
31085                 }
31086                 this.execCmd('styleWithCSS', true); // 
31087                 break;
31088             case 'bold':
31089             case 'italic':
31090                 // if there is no selection, then we insert, and set the curson inside it..
31091                 this.execCmd('styleWithCSS', false); 
31092                 break;
31093                 
31094         
31095             default:
31096                 break;
31097         }
31098         
31099         
31100         this.win.focus();
31101         this.execCmd(cmd, value);
31102         this.owner.fireEvent('editorevent', this);
31103         //this.updateToolbar();
31104         this.owner.deferFocus();
31105     },
31106
31107     /**
31108      * Executes a Midas editor command directly on the editor document.
31109      * For visual commands, you should use {@link #relayCmd} instead.
31110      * <b>This should only be called after the editor is initialized.</b>
31111      * @param {String} cmd The Midas command
31112      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31113      */
31114     execCmd : function(cmd, value){
31115         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31116         this.syncValue();
31117     },
31118  
31119  
31120    
31121     /**
31122      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31123      * to insert tRoo.
31124      * @param {String} text | dom node.. 
31125      */
31126     insertAtCursor : function(text)
31127     {
31128         
31129         if(!this.activated){
31130             return;
31131         }
31132          
31133         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31134             this.win.focus();
31135             
31136             
31137             // from jquery ui (MIT licenced)
31138             var range, node;
31139             var win = this.win;
31140             
31141             if (win.getSelection && win.getSelection().getRangeAt) {
31142                 
31143                 // delete the existing?
31144                 
31145                 this.createRange(this.getSelection()).deleteContents();
31146                 range = win.getSelection().getRangeAt(0);
31147                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31148                 range.insertNode(node);
31149                 range = range.cloneRange();
31150                 range.collapse(false);
31151                  
31152                 win.getSelection().removeAllRanges();
31153                 win.getSelection().addRange(range);
31154                 
31155                 
31156                 
31157             } else if (win.document.selection && win.document.selection.createRange) {
31158                 // no firefox support
31159                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31160                 win.document.selection.createRange().pasteHTML(txt);
31161             
31162             } else {
31163                 // no firefox support
31164                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31165                 this.execCmd('InsertHTML', txt);
31166             } 
31167             this.syncValue();
31168             
31169             this.deferFocus();
31170         }
31171     },
31172  // private
31173     mozKeyPress : function(e){
31174         if(e.ctrlKey){
31175             var c = e.getCharCode(), cmd;
31176           
31177             if(c > 0){
31178                 c = String.fromCharCode(c).toLowerCase();
31179                 switch(c){
31180                     case 'b':
31181                         cmd = 'bold';
31182                         break;
31183                     case 'i':
31184                         cmd = 'italic';
31185                         break;
31186                     
31187                     case 'u':
31188                         cmd = 'underline';
31189                         break;
31190                     
31191                     //case 'v':
31192                       //  this.cleanUpPaste.defer(100, this);
31193                       //  return;
31194                         
31195                 }
31196                 if(cmd){
31197                     
31198                     this.relayCmd(cmd);
31199                     //this.win.focus();
31200                     //this.execCmd(cmd);
31201                     //this.deferFocus();
31202                     e.preventDefault();
31203                 }
31204                 
31205             }
31206         }
31207     },
31208
31209     // private
31210     fixKeys : function(){ // load time branching for fastest keydown performance
31211         
31212         
31213         if(Roo.isIE){
31214             return function(e){
31215                 var k = e.getKey(), r;
31216                 if(k == e.TAB){
31217                     e.stopEvent();
31218                     r = this.doc.selection.createRange();
31219                     if(r){
31220                         r.collapse(true);
31221                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31222                         this.deferFocus();
31223                     }
31224                     return;
31225                 }
31226                 /// this is handled by Roo.htmleditor.KeyEnter
31227                  /*
31228                 if(k == e.ENTER){
31229                     r = this.doc.selection.createRange();
31230                     if(r){
31231                         var target = r.parentElement();
31232                         if(!target || target.tagName.toLowerCase() != 'li'){
31233                             e.stopEvent();
31234                             r.pasteHTML('<br/>');
31235                             r.collapse(false);
31236                             r.select();
31237                         }
31238                     }
31239                 }
31240                 */
31241                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31242                 //    this.cleanUpPaste.defer(100, this);
31243                 //    return;
31244                 //}
31245                 
31246                 
31247             };
31248         }else if(Roo.isOpera){
31249             return function(e){
31250                 var k = e.getKey();
31251                 if(k == e.TAB){
31252                     e.stopEvent();
31253                     this.win.focus();
31254                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31255                     this.deferFocus();
31256                 }
31257                
31258                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31259                 //    this.cleanUpPaste.defer(100, this);
31260                  //   return;
31261                 //}
31262                 
31263             };
31264         }else if(Roo.isSafari){
31265             return function(e){
31266                 var k = e.getKey();
31267                 
31268                 if(k == e.TAB){
31269                     e.stopEvent();
31270                     this.execCmd('InsertText','\t');
31271                     this.deferFocus();
31272                     return;
31273                 }
31274                  this.mozKeyPress(e);
31275                 
31276                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31277                  //   this.cleanUpPaste.defer(100, this);
31278                  //   return;
31279                // }
31280                 
31281              };
31282         }
31283     }(),
31284     
31285     getAllAncestors: function()
31286     {
31287         var p = this.getSelectedNode();
31288         var a = [];
31289         if (!p) {
31290             a.push(p); // push blank onto stack..
31291             p = this.getParentElement();
31292         }
31293         
31294         
31295         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31296             a.push(p);
31297             p = p.parentNode;
31298         }
31299         a.push(this.doc.body);
31300         return a;
31301     },
31302     lastSel : false,
31303     lastSelNode : false,
31304     
31305     
31306     getSelection : function() 
31307     {
31308         this.assignDocWin();
31309         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31310     },
31311     /**
31312      * Select a dom node
31313      * @param {DomElement} node the node to select
31314      */
31315     selectNode : function(node, collapse)
31316     {
31317         var nodeRange = node.ownerDocument.createRange();
31318         try {
31319             nodeRange.selectNode(node);
31320         } catch (e) {
31321             nodeRange.selectNodeContents(node);
31322         }
31323         if (collapse === true) {
31324             nodeRange.collapse(true);
31325         }
31326         //
31327         var s = this.win.getSelection();
31328         s.removeAllRanges();
31329         s.addRange(nodeRange);
31330     },
31331     
31332     getSelectedNode: function() 
31333     {
31334         // this may only work on Gecko!!!
31335         
31336         // should we cache this!!!!
31337         
31338          
31339          
31340         var range = this.createRange(this.getSelection()).cloneRange();
31341         
31342         if (Roo.isIE) {
31343             var parent = range.parentElement();
31344             while (true) {
31345                 var testRange = range.duplicate();
31346                 testRange.moveToElementText(parent);
31347                 if (testRange.inRange(range)) {
31348                     break;
31349                 }
31350                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31351                     break;
31352                 }
31353                 parent = parent.parentElement;
31354             }
31355             return parent;
31356         }
31357         
31358         // is ancestor a text element.
31359         var ac =  range.commonAncestorContainer;
31360         if (ac.nodeType == 3) {
31361             ac = ac.parentNode;
31362         }
31363         
31364         var ar = ac.childNodes;
31365          
31366         var nodes = [];
31367         var other_nodes = [];
31368         var has_other_nodes = false;
31369         for (var i=0;i<ar.length;i++) {
31370             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31371                 continue;
31372             }
31373             // fullly contained node.
31374             
31375             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31376                 nodes.push(ar[i]);
31377                 continue;
31378             }
31379             
31380             // probably selected..
31381             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31382                 other_nodes.push(ar[i]);
31383                 continue;
31384             }
31385             // outer..
31386             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31387                 continue;
31388             }
31389             
31390             
31391             has_other_nodes = true;
31392         }
31393         if (!nodes.length && other_nodes.length) {
31394             nodes= other_nodes;
31395         }
31396         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31397             return false;
31398         }
31399         
31400         return nodes[0];
31401     },
31402     
31403     
31404     createRange: function(sel)
31405     {
31406         // this has strange effects when using with 
31407         // top toolbar - not sure if it's a great idea.
31408         //this.editor.contentWindow.focus();
31409         if (typeof sel != "undefined") {
31410             try {
31411                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31412             } catch(e) {
31413                 return this.doc.createRange();
31414             }
31415         } else {
31416             return this.doc.createRange();
31417         }
31418     },
31419     getParentElement: function()
31420     {
31421         
31422         this.assignDocWin();
31423         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31424         
31425         var range = this.createRange(sel);
31426          
31427         try {
31428             var p = range.commonAncestorContainer;
31429             while (p.nodeType == 3) { // text node
31430                 p = p.parentNode;
31431             }
31432             return p;
31433         } catch (e) {
31434             return null;
31435         }
31436     
31437     },
31438     /***
31439      *
31440      * Range intersection.. the hard stuff...
31441      *  '-1' = before
31442      *  '0' = hits..
31443      *  '1' = after.
31444      *         [ -- selected range --- ]
31445      *   [fail]                        [fail]
31446      *
31447      *    basically..
31448      *      if end is before start or  hits it. fail.
31449      *      if start is after end or hits it fail.
31450      *
31451      *   if either hits (but other is outside. - then it's not 
31452      *   
31453      *    
31454      **/
31455     
31456     
31457     // @see http://www.thismuchiknow.co.uk/?p=64.
31458     rangeIntersectsNode : function(range, node)
31459     {
31460         var nodeRange = node.ownerDocument.createRange();
31461         try {
31462             nodeRange.selectNode(node);
31463         } catch (e) {
31464             nodeRange.selectNodeContents(node);
31465         }
31466     
31467         var rangeStartRange = range.cloneRange();
31468         rangeStartRange.collapse(true);
31469     
31470         var rangeEndRange = range.cloneRange();
31471         rangeEndRange.collapse(false);
31472     
31473         var nodeStartRange = nodeRange.cloneRange();
31474         nodeStartRange.collapse(true);
31475     
31476         var nodeEndRange = nodeRange.cloneRange();
31477         nodeEndRange.collapse(false);
31478     
31479         return rangeStartRange.compareBoundaryPoints(
31480                  Range.START_TO_START, nodeEndRange) == -1 &&
31481                rangeEndRange.compareBoundaryPoints(
31482                  Range.START_TO_START, nodeStartRange) == 1;
31483         
31484          
31485     },
31486     rangeCompareNode : function(range, node)
31487     {
31488         var nodeRange = node.ownerDocument.createRange();
31489         try {
31490             nodeRange.selectNode(node);
31491         } catch (e) {
31492             nodeRange.selectNodeContents(node);
31493         }
31494         
31495         
31496         range.collapse(true);
31497     
31498         nodeRange.collapse(true);
31499      
31500         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31501         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
31502          
31503         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31504         
31505         var nodeIsBefore   =  ss == 1;
31506         var nodeIsAfter    = ee == -1;
31507         
31508         if (nodeIsBefore && nodeIsAfter) {
31509             return 0; // outer
31510         }
31511         if (!nodeIsBefore && nodeIsAfter) {
31512             return 1; //right trailed.
31513         }
31514         
31515         if (nodeIsBefore && !nodeIsAfter) {
31516             return 2;  // left trailed.
31517         }
31518         // fully contined.
31519         return 3;
31520     },
31521  
31522     cleanWordChars : function(input) {// change the chars to hex code
31523         
31524        var swapCodes  = [ 
31525             [    8211, "&#8211;" ], 
31526             [    8212, "&#8212;" ], 
31527             [    8216,  "'" ],  
31528             [    8217, "'" ],  
31529             [    8220, '"' ],  
31530             [    8221, '"' ],  
31531             [    8226, "*" ],  
31532             [    8230, "..." ]
31533         ]; 
31534         var output = input;
31535         Roo.each(swapCodes, function(sw) { 
31536             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
31537             
31538             output = output.replace(swapper, sw[1]);
31539         });
31540         
31541         return output;
31542     },
31543     
31544      
31545     
31546         
31547     
31548     cleanUpChild : function (node)
31549     {
31550         
31551         new Roo.htmleditor.FilterComment({node : node});
31552         new Roo.htmleditor.FilterAttributes({
31553                 node : node,
31554                 attrib_black : this.ablack,
31555                 attrib_clean : this.aclean,
31556                 style_white : this.cwhite,
31557                 style_black : this.cblack
31558         });
31559         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
31560         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
31561          
31562         
31563     },
31564     
31565     /**
31566      * Clean up MS wordisms...
31567      * @deprecated - use filter directly
31568      */
31569     cleanWord : function(node)
31570     {
31571         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
31572         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
31573         
31574     },
31575    
31576     
31577     /**
31578
31579      * @deprecated - use filters
31580      */
31581     cleanTableWidths : function(node)
31582     {
31583         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
31584         
31585  
31586     },
31587     
31588      
31589         
31590     applyBlacklists : function()
31591     {
31592         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
31593         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
31594         
31595         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
31596         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
31597         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
31598         
31599         this.white = [];
31600         this.black = [];
31601         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
31602             if (b.indexOf(tag) > -1) {
31603                 return;
31604             }
31605             this.white.push(tag);
31606             
31607         }, this);
31608         
31609         Roo.each(w, function(tag) {
31610             if (b.indexOf(tag) > -1) {
31611                 return;
31612             }
31613             if (this.white.indexOf(tag) > -1) {
31614                 return;
31615             }
31616             this.white.push(tag);
31617             
31618         }, this);
31619         
31620         
31621         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
31622             if (w.indexOf(tag) > -1) {
31623                 return;
31624             }
31625             this.black.push(tag);
31626             
31627         }, this);
31628         
31629         Roo.each(b, function(tag) {
31630             if (w.indexOf(tag) > -1) {
31631                 return;
31632             }
31633             if (this.black.indexOf(tag) > -1) {
31634                 return;
31635             }
31636             this.black.push(tag);
31637             
31638         }, this);
31639         
31640         
31641         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
31642         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
31643         
31644         this.cwhite = [];
31645         this.cblack = [];
31646         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
31647             if (b.indexOf(tag) > -1) {
31648                 return;
31649             }
31650             this.cwhite.push(tag);
31651             
31652         }, this);
31653         
31654         Roo.each(w, function(tag) {
31655             if (b.indexOf(tag) > -1) {
31656                 return;
31657             }
31658             if (this.cwhite.indexOf(tag) > -1) {
31659                 return;
31660             }
31661             this.cwhite.push(tag);
31662             
31663         }, this);
31664         
31665         
31666         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
31667             if (w.indexOf(tag) > -1) {
31668                 return;
31669             }
31670             this.cblack.push(tag);
31671             
31672         }, this);
31673         
31674         Roo.each(b, function(tag) {
31675             if (w.indexOf(tag) > -1) {
31676                 return;
31677             }
31678             if (this.cblack.indexOf(tag) > -1) {
31679                 return;
31680             }
31681             this.cblack.push(tag);
31682             
31683         }, this);
31684     },
31685     
31686     setStylesheets : function(stylesheets)
31687     {
31688         if(typeof(stylesheets) == 'string'){
31689             Roo.get(this.iframe.contentDocument.head).createChild({
31690                 tag : 'link',
31691                 rel : 'stylesheet',
31692                 type : 'text/css',
31693                 href : stylesheets
31694             });
31695             
31696             return;
31697         }
31698         var _this = this;
31699      
31700         Roo.each(stylesheets, function(s) {
31701             if(!s.length){
31702                 return;
31703             }
31704             
31705             Roo.get(_this.iframe.contentDocument.head).createChild({
31706                 tag : 'link',
31707                 rel : 'stylesheet',
31708                 type : 'text/css',
31709                 href : s
31710             });
31711         });
31712
31713         
31714     },
31715     
31716     
31717     updateLanguage : function()
31718     {
31719         if (!this.iframe || !this.iframe.contentDocument) {
31720             return;
31721         }
31722         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
31723     },
31724     
31725     
31726     removeStylesheets : function()
31727     {
31728         var _this = this;
31729         
31730         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
31731             s.remove();
31732         });
31733     },
31734     
31735     setStyle : function(style)
31736     {
31737         Roo.get(this.iframe.contentDocument.head).createChild({
31738             tag : 'style',
31739             type : 'text/css',
31740             html : style
31741         });
31742
31743         return;
31744     }
31745     
31746     // hide stuff that is not compatible
31747     /**
31748      * @event blur
31749      * @hide
31750      */
31751     /**
31752      * @event change
31753      * @hide
31754      */
31755     /**
31756      * @event focus
31757      * @hide
31758      */
31759     /**
31760      * @event specialkey
31761      * @hide
31762      */
31763     /**
31764      * @cfg {String} fieldClass @hide
31765      */
31766     /**
31767      * @cfg {String} focusClass @hide
31768      */
31769     /**
31770      * @cfg {String} autoCreate @hide
31771      */
31772     /**
31773      * @cfg {String} inputType @hide
31774      */
31775     /**
31776      * @cfg {String} invalidClass @hide
31777      */
31778     /**
31779      * @cfg {String} invalidText @hide
31780      */
31781     /**
31782      * @cfg {String} msgFx @hide
31783      */
31784     /**
31785      * @cfg {String} validateOnBlur @hide
31786      */
31787 });
31788
31789 Roo.HtmlEditorCore.white = [
31790         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
31791         
31792        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
31793        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
31794        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
31795        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
31796        'TABLE',   'UL',         'XMP', 
31797        
31798        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
31799       'THEAD',   'TR', 
31800      
31801       'DIR', 'MENU', 'OL', 'UL', 'DL',
31802        
31803       'EMBED',  'OBJECT'
31804 ];
31805
31806
31807 Roo.HtmlEditorCore.black = [
31808     //    'embed',  'object', // enable - backend responsiblity to clean thiese
31809         'APPLET', // 
31810         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
31811         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
31812         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
31813         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
31814         //'FONT' // CLEAN LATER..
31815         'COLGROUP', 'COL'   // messy tables.
31816         
31817         
31818 ];
31819 Roo.HtmlEditorCore.clean = [ // ?? needed???
31820      'SCRIPT', 'STYLE', 'TITLE', 'XML'
31821 ];
31822 Roo.HtmlEditorCore.tag_remove = [
31823     'FONT', 'TBODY'  
31824 ];
31825 // attributes..
31826
31827 Roo.HtmlEditorCore.ablack = [
31828     'on'
31829 ];
31830     
31831 Roo.HtmlEditorCore.aclean = [ 
31832     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
31833 ];
31834
31835 // protocols..
31836 Roo.HtmlEditorCore.pwhite= [
31837         'http',  'https',  'mailto'
31838 ];
31839
31840 // white listed style attributes.
31841 Roo.HtmlEditorCore.cwhite= [
31842       //  'text-align', /// default is to allow most things..
31843       
31844          
31845 //        'font-size'//??
31846 ];
31847
31848 // black listed style attributes.
31849 Roo.HtmlEditorCore.cblack= [
31850       //  'font-size' -- this can be set by the project 
31851 ];
31852
31853
31854
31855
31856     /*
31857  * - LGPL
31858  *
31859  * HtmlEditor
31860  * 
31861  */
31862
31863 /**
31864  * @class Roo.bootstrap.form.HtmlEditor
31865  * @extends Roo.bootstrap.form.TextArea
31866  * Bootstrap HtmlEditor class
31867
31868  * @constructor
31869  * Create a new HtmlEditor
31870  * @param {Object} config The config object
31871  */
31872
31873 Roo.bootstrap.form.HtmlEditor = function(config){
31874     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
31875     if (!this.toolbars) {
31876         this.toolbars = [];
31877     }
31878     
31879     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
31880     this.addEvents({
31881             /**
31882              * @event initialize
31883              * Fires when the editor is fully initialized (including the iframe)
31884              * @param {HtmlEditor} this
31885              */
31886             initialize: true,
31887             /**
31888              * @event activate
31889              * Fires when the editor is first receives the focus. Any insertion must wait
31890              * until after this event.
31891              * @param {HtmlEditor} this
31892              */
31893             activate: true,
31894              /**
31895              * @event beforesync
31896              * Fires before the textarea is updated with content from the editor iframe. Return false
31897              * to cancel the sync.
31898              * @param {HtmlEditor} this
31899              * @param {String} html
31900              */
31901             beforesync: true,
31902              /**
31903              * @event beforepush
31904              * Fires before the iframe editor is updated with content from the textarea. Return false
31905              * to cancel the push.
31906              * @param {HtmlEditor} this
31907              * @param {String} html
31908              */
31909             beforepush: true,
31910              /**
31911              * @event sync
31912              * Fires when the textarea is updated with content from the editor iframe.
31913              * @param {HtmlEditor} this
31914              * @param {String} html
31915              */
31916             sync: true,
31917              /**
31918              * @event push
31919              * Fires when the iframe editor is updated with content from the textarea.
31920              * @param {HtmlEditor} this
31921              * @param {String} html
31922              */
31923             push: true,
31924              /**
31925              * @event editmodechange
31926              * Fires when the editor switches edit modes
31927              * @param {HtmlEditor} this
31928              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
31929              */
31930             editmodechange: true,
31931             /**
31932              * @event editorevent
31933              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31934              * @param {HtmlEditor} this
31935              */
31936             editorevent: true,
31937             /**
31938              * @event firstfocus
31939              * Fires when on first focus - needed by toolbars..
31940              * @param {HtmlEditor} this
31941              */
31942             firstfocus: true,
31943             /**
31944              * @event autosave
31945              * Auto save the htmlEditor value as a file into Events
31946              * @param {HtmlEditor} this
31947              */
31948             autosave: true,
31949             /**
31950              * @event savedpreview
31951              * preview the saved version of htmlEditor
31952              * @param {HtmlEditor} this
31953              */
31954             savedpreview: true
31955         });
31956 };
31957
31958
31959 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
31960     
31961     
31962       /**
31963      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
31964      */
31965     toolbars : false,
31966     
31967      /**
31968     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
31969     */
31970     btns : [],
31971    
31972      /**
31973      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
31974      *                        Roo.resizable.
31975      */
31976     resizable : false,
31977      /**
31978      * @cfg {Number} height (in pixels)
31979      */   
31980     height: 300,
31981    /**
31982      * @cfg {Number} width (in pixels)
31983      */   
31984     width: false,
31985     
31986     /**
31987      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
31988      * 
31989      */
31990     stylesheets: false,
31991     
31992     // id of frame..
31993     frameId: false,
31994     
31995     // private properties
31996     validationEvent : false,
31997     deferHeight: true,
31998     initialized : false,
31999     activated : false,
32000     
32001     onFocus : Roo.emptyFn,
32002     iframePad:3,
32003     hideMode:'offsets',
32004     
32005     tbContainer : false,
32006     
32007     bodyCls : '',
32008     
32009     toolbarContainer :function() {
32010         return this.wrap.select('.x-html-editor-tb',true).first();
32011     },
32012
32013     /**
32014      * Protected method that will not generally be called directly. It
32015      * is called when the editor creates its toolbar. Override this method if you need to
32016      * add custom toolbar buttons.
32017      * @param {HtmlEditor} editor
32018      */
32019     createToolbar : function(){
32020         Roo.log('renewing');
32021         Roo.log("create toolbars");
32022         
32023         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32024         this.toolbars[0].render(this.toolbarContainer());
32025         
32026         return;
32027         
32028 //        if (!editor.toolbars || !editor.toolbars.length) {
32029 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32030 //        }
32031 //        
32032 //        for (var i =0 ; i < editor.toolbars.length;i++) {
32033 //            editor.toolbars[i] = Roo.factory(
32034 //                    typeof(editor.toolbars[i]) == 'string' ?
32035 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
32036 //                Roo.bootstrap.form.HtmlEditor);
32037 //            editor.toolbars[i].init(editor);
32038 //        }
32039     },
32040
32041      
32042     // private
32043     onRender : function(ct, position)
32044     {
32045        // Roo.log("Call onRender: " + this.xtype);
32046         var _t = this;
32047         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32048       
32049         this.wrap = this.inputEl().wrap({
32050             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32051         });
32052         
32053         this.editorcore.onRender(ct, position);
32054          
32055         if (this.resizable) {
32056             this.resizeEl = new Roo.Resizable(this.wrap, {
32057                 pinned : true,
32058                 wrap: true,
32059                 dynamic : true,
32060                 minHeight : this.height,
32061                 height: this.height,
32062                 handles : this.resizable,
32063                 width: this.width,
32064                 listeners : {
32065                     resize : function(r, w, h) {
32066                         _t.onResize(w,h); // -something
32067                     }
32068                 }
32069             });
32070             
32071         }
32072         this.createToolbar(this);
32073        
32074         
32075         if(!this.width && this.resizable){
32076             this.setSize(this.wrap.getSize());
32077         }
32078         if (this.resizeEl) {
32079             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32080             // should trigger onReize..
32081         }
32082         
32083     },
32084
32085     // private
32086     onResize : function(w, h)
32087     {
32088         Roo.log('resize: ' +w + ',' + h );
32089         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32090         var ew = false;
32091         var eh = false;
32092         
32093         if(this.inputEl() ){
32094             if(typeof w == 'number'){
32095                 var aw = w - this.wrap.getFrameWidth('lr');
32096                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32097                 ew = aw;
32098             }
32099             if(typeof h == 'number'){
32100                  var tbh = -11;  // fixme it needs to tool bar size!
32101                 for (var i =0; i < this.toolbars.length;i++) {
32102                     // fixme - ask toolbars for heights?
32103                     tbh += this.toolbars[i].el.getHeight();
32104                     //if (this.toolbars[i].footer) {
32105                     //    tbh += this.toolbars[i].footer.el.getHeight();
32106                     //}
32107                 }
32108               
32109                 
32110                 
32111                 
32112                 
32113                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32114                 ah -= 5; // knock a few pixes off for look..
32115                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32116                 var eh = ah;
32117             }
32118         }
32119         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32120         this.editorcore.onResize(ew,eh);
32121         
32122     },
32123
32124     /**
32125      * Toggles the editor between standard and source edit mode.
32126      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32127      */
32128     toggleSourceEdit : function(sourceEditMode)
32129     {
32130         this.editorcore.toggleSourceEdit(sourceEditMode);
32131         
32132         if(this.editorcore.sourceEditMode){
32133             Roo.log('editor - showing textarea');
32134             
32135 //            Roo.log('in');
32136 //            Roo.log(this.syncValue());
32137             this.syncValue();
32138             this.inputEl().removeClass(['hide', 'x-hidden']);
32139             this.inputEl().dom.removeAttribute('tabIndex');
32140             this.inputEl().focus();
32141         }else{
32142             Roo.log('editor - hiding textarea');
32143 //            Roo.log('out')
32144 //            Roo.log(this.pushValue()); 
32145             this.pushValue();
32146             
32147             this.inputEl().addClass(['hide', 'x-hidden']);
32148             this.inputEl().dom.setAttribute('tabIndex', -1);
32149             //this.deferFocus();
32150         }
32151          
32152         if(this.resizable){
32153             this.setSize(this.wrap.getSize());
32154         }
32155         
32156         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32157     },
32158  
32159     // private (for BoxComponent)
32160     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32161
32162     // private (for BoxComponent)
32163     getResizeEl : function(){
32164         return this.wrap;
32165     },
32166
32167     // private (for BoxComponent)
32168     getPositionEl : function(){
32169         return this.wrap;
32170     },
32171
32172     // private
32173     initEvents : function(){
32174         this.originalValue = this.getValue();
32175     },
32176
32177 //    /**
32178 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32179 //     * @method
32180 //     */
32181 //    markInvalid : Roo.emptyFn,
32182 //    /**
32183 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32184 //     * @method
32185 //     */
32186 //    clearInvalid : Roo.emptyFn,
32187
32188     setValue : function(v){
32189         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32190         this.editorcore.pushValue();
32191     },
32192
32193      
32194     // private
32195     deferFocus : function(){
32196         this.focus.defer(10, this);
32197     },
32198
32199     // doc'ed in Field
32200     focus : function(){
32201         this.editorcore.focus();
32202         
32203     },
32204       
32205
32206     // private
32207     onDestroy : function(){
32208         
32209         
32210         
32211         if(this.rendered){
32212             
32213             for (var i =0; i < this.toolbars.length;i++) {
32214                 // fixme - ask toolbars for heights?
32215                 this.toolbars[i].onDestroy();
32216             }
32217             
32218             this.wrap.dom.innerHTML = '';
32219             this.wrap.remove();
32220         }
32221     },
32222
32223     // private
32224     onFirstFocus : function(){
32225         //Roo.log("onFirstFocus");
32226         this.editorcore.onFirstFocus();
32227          for (var i =0; i < this.toolbars.length;i++) {
32228             this.toolbars[i].onFirstFocus();
32229         }
32230         
32231     },
32232     
32233     // private
32234     syncValue : function()
32235     {   
32236         this.editorcore.syncValue();
32237     },
32238     
32239     pushValue : function()
32240     {   
32241         this.editorcore.pushValue();
32242     }
32243      
32244     
32245     // hide stuff that is not compatible
32246     /**
32247      * @event blur
32248      * @hide
32249      */
32250     /**
32251      * @event change
32252      * @hide
32253      */
32254     /**
32255      * @event focus
32256      * @hide
32257      */
32258     /**
32259      * @event specialkey
32260      * @hide
32261      */
32262     /**
32263      * @cfg {String} fieldClass @hide
32264      */
32265     /**
32266      * @cfg {String} focusClass @hide
32267      */
32268     /**
32269      * @cfg {String} autoCreate @hide
32270      */
32271     /**
32272      * @cfg {String} inputType @hide
32273      */
32274      
32275     /**
32276      * @cfg {String} invalidText @hide
32277      */
32278     /**
32279      * @cfg {String} msgFx @hide
32280      */
32281     /**
32282      * @cfg {String} validateOnBlur @hide
32283      */
32284 });
32285  
32286     
32287    
32288    
32289    
32290       
32291 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32292 /**
32293  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32294  * @parent Roo.bootstrap.form.HtmlEditor
32295  * @extends Roo.bootstrap.nav.Simplebar
32296  * Basic Toolbar
32297  * 
32298  * @example
32299  * Usage:
32300  *
32301  new Roo.bootstrap.form.HtmlEditor({
32302     ....
32303     toolbars : [
32304         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32305             disable : { fonts: 1 , format: 1, ..., ... , ...],
32306             btns : [ .... ]
32307         })
32308     }
32309      
32310  * 
32311  * @cfg {Object} disable List of elements to disable..
32312  * @cfg {Array} btns List of additional buttons.
32313  * 
32314  * 
32315  * NEEDS Extra CSS? 
32316  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32317  */
32318  
32319 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32320 {
32321     
32322     Roo.apply(this, config);
32323     
32324     // default disabled, based on 'good practice'..
32325     this.disable = this.disable || {};
32326     Roo.applyIf(this.disable, {
32327         fontSize : true,
32328         colors : true,
32329         specialElements : true
32330     });
32331     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32332     
32333     this.editor = config.editor;
32334     this.editorcore = config.editor.editorcore;
32335     
32336     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32337     
32338     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32339     // dont call parent... till later.
32340 }
32341 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32342      
32343     bar : true,
32344     
32345     editor : false,
32346     editorcore : false,
32347     
32348     
32349     formats : [
32350         "p" ,  
32351         "h1","h2","h3","h4","h5","h6", 
32352         "pre", "code", 
32353         "abbr", "acronym", "address", "cite", "samp", "var",
32354         'div','span'
32355     ],
32356     
32357     onRender : function(ct, position)
32358     {
32359        // Roo.log("Call onRender: " + this.xtype);
32360         
32361        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32362        Roo.log(this.el);
32363        this.el.dom.style.marginBottom = '0';
32364        var _this = this;
32365        var editorcore = this.editorcore;
32366        var editor= this.editor;
32367        
32368        var children = [];
32369        var btn = function(id,cmd , toggle, handler, html){
32370        
32371             var  event = toggle ? 'toggle' : 'click';
32372        
32373             var a = {
32374                 size : 'sm',
32375                 xtype: 'Button',
32376                 xns: Roo.bootstrap,
32377                 //glyphicon : id,
32378                 fa: id,
32379                 cmd : id || cmd,
32380                 enableToggle:toggle !== false,
32381                 html : html || '',
32382                 pressed : toggle ? false : null,
32383                 listeners : {}
32384             };
32385             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32386                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32387             };
32388             children.push(a);
32389             return a;
32390        }
32391        
32392     //    var cb_box = function...
32393         
32394         var style = {
32395                 xtype: 'Button',
32396                 size : 'sm',
32397                 xns: Roo.bootstrap,
32398                 fa : 'font',
32399                 //html : 'submit'
32400                 menu : {
32401                     xtype: 'Menu',
32402                     xns: Roo.bootstrap,
32403                     items:  []
32404                 }
32405         };
32406         Roo.each(this.formats, function(f) {
32407             style.menu.items.push({
32408                 xtype :'MenuItem',
32409                 xns: Roo.bootstrap,
32410                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32411                 tagname : f,
32412                 listeners : {
32413                     click : function()
32414                     {
32415                         editorcore.insertTag(this.tagname);
32416                         editor.focus();
32417                     }
32418                 }
32419                 
32420             });
32421         });
32422         children.push(style);   
32423         
32424         btn('bold',false,true);
32425         btn('italic',false,true);
32426         btn('align-left', 'justifyleft',true);
32427         btn('align-center', 'justifycenter',true);
32428         btn('align-right' , 'justifyright',true);
32429         btn('link', false, false, function(btn) {
32430             //Roo.log("create link?");
32431             var url = prompt(this.createLinkText, this.defaultLinkValue);
32432             if(url && url != 'http:/'+'/'){
32433                 this.editorcore.relayCmd('createlink', url);
32434             }
32435         }),
32436         btn('list','insertunorderedlist',true);
32437         btn('pencil', false,true, function(btn){
32438                 Roo.log(this);
32439                 this.toggleSourceEdit(btn.pressed);
32440         });
32441         
32442         if (this.editor.btns.length > 0) {
32443             for (var i = 0; i<this.editor.btns.length; i++) {
32444                 children.push(this.editor.btns[i]);
32445             }
32446         }
32447         
32448         /*
32449         var cog = {
32450                 xtype: 'Button',
32451                 size : 'sm',
32452                 xns: Roo.bootstrap,
32453                 glyphicon : 'cog',
32454                 //html : 'submit'
32455                 menu : {
32456                     xtype: 'Menu',
32457                     xns: Roo.bootstrap,
32458                     items:  []
32459                 }
32460         };
32461         
32462         cog.menu.items.push({
32463             xtype :'MenuItem',
32464             xns: Roo.bootstrap,
32465             html : Clean styles,
32466             tagname : f,
32467             listeners : {
32468                 click : function()
32469                 {
32470                     editorcore.insertTag(this.tagname);
32471                     editor.focus();
32472                 }
32473             }
32474             
32475         });
32476        */
32477         
32478          
32479        this.xtype = 'NavSimplebar';
32480         
32481         for(var i=0;i< children.length;i++) {
32482             
32483             this.buttons.add(this.addxtypeChild(children[i]));
32484             
32485         }
32486         
32487         editor.on('editorevent', this.updateToolbar, this);
32488     },
32489     onBtnClick : function(id)
32490     {
32491        this.editorcore.relayCmd(id);
32492        this.editorcore.focus();
32493     },
32494     
32495     /**
32496      * Protected method that will not generally be called directly. It triggers
32497      * a toolbar update by reading the markup state of the current selection in the editor.
32498      */
32499     updateToolbar: function(){
32500
32501         if(!this.editorcore.activated){
32502             this.editor.onFirstFocus(); // is this neeed?
32503             return;
32504         }
32505
32506         var btns = this.buttons; 
32507         var doc = this.editorcore.doc;
32508         btns.get('bold').setActive(doc.queryCommandState('bold'));
32509         btns.get('italic').setActive(doc.queryCommandState('italic'));
32510         //btns.get('underline').setActive(doc.queryCommandState('underline'));
32511         
32512         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32513         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32514         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32515         
32516         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32517         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32518          /*
32519         
32520         var ans = this.editorcore.getAllAncestors();
32521         if (this.formatCombo) {
32522             
32523             
32524             var store = this.formatCombo.store;
32525             this.formatCombo.setValue("");
32526             for (var i =0; i < ans.length;i++) {
32527                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32528                     // select it..
32529                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
32530                     break;
32531                 }
32532             }
32533         }
32534         
32535         
32536         
32537         // hides menus... - so this cant be on a menu...
32538         Roo.bootstrap.MenuMgr.hideAll();
32539         */
32540         Roo.bootstrap.menu.Manager.hideAll();
32541         //this.editorsyncValue();
32542     },
32543     onFirstFocus: function() {
32544         this.buttons.each(function(item){
32545            item.enable();
32546         });
32547     },
32548     toggleSourceEdit : function(sourceEditMode){
32549         
32550           
32551         if(sourceEditMode){
32552             Roo.log("disabling buttons");
32553            this.buttons.each( function(item){
32554                 if(item.cmd != 'pencil'){
32555                     item.disable();
32556                 }
32557             });
32558           
32559         }else{
32560             Roo.log("enabling buttons");
32561             if(this.editorcore.initialized){
32562                 this.buttons.each( function(item){
32563                     item.enable();
32564                 });
32565             }
32566             
32567         }
32568         Roo.log("calling toggole on editor");
32569         // tell the editor that it's been pressed..
32570         this.editor.toggleSourceEdit(sourceEditMode);
32571        
32572     }
32573 });
32574
32575
32576
32577
32578  
32579 /*
32580  * - LGPL
32581  */
32582
32583 /**
32584  * @class Roo.bootstrap.form.Markdown
32585  * @extends Roo.bootstrap.form.TextArea
32586  * Bootstrap Showdown editable area
32587  * @cfg {string} content
32588  * 
32589  * @constructor
32590  * Create a new Showdown
32591  */
32592
32593 Roo.bootstrap.form.Markdown = function(config){
32594     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
32595    
32596 };
32597
32598 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
32599     
32600     editing :false,
32601     
32602     initEvents : function()
32603     {
32604         
32605         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
32606         this.markdownEl = this.el.createChild({
32607             cls : 'roo-markdown-area'
32608         });
32609         this.inputEl().addClass('d-none');
32610         if (this.getValue() == '') {
32611             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32612             
32613         } else {
32614             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32615         }
32616         this.markdownEl.on('click', this.toggleTextEdit, this);
32617         this.on('blur', this.toggleTextEdit, this);
32618         this.on('specialkey', this.resizeTextArea, this);
32619     },
32620     
32621     toggleTextEdit : function()
32622     {
32623         var sh = this.markdownEl.getHeight();
32624         this.inputEl().addClass('d-none');
32625         this.markdownEl.addClass('d-none');
32626         if (!this.editing) {
32627             // show editor?
32628             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
32629             this.inputEl().removeClass('d-none');
32630             this.inputEl().focus();
32631             this.editing = true;
32632             return;
32633         }
32634         // show showdown...
32635         this.updateMarkdown();
32636         this.markdownEl.removeClass('d-none');
32637         this.editing = false;
32638         return;
32639     },
32640     updateMarkdown : function()
32641     {
32642         if (this.getValue() == '') {
32643             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32644             return;
32645         }
32646  
32647         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32648     },
32649     
32650     resizeTextArea: function () {
32651         
32652         var sh = 100;
32653         Roo.log([sh, this.getValue().split("\n").length * 30]);
32654         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
32655     },
32656     setValue : function(val)
32657     {
32658         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
32659         if (!this.editing) {
32660             this.updateMarkdown();
32661         }
32662         
32663     },
32664     focus : function()
32665     {
32666         if (!this.editing) {
32667             this.toggleTextEdit();
32668         }
32669         
32670     }
32671
32672
32673 });/*
32674  * Based on:
32675  * Ext JS Library 1.1.1
32676  * Copyright(c) 2006-2007, Ext JS, LLC.
32677  *
32678  * Originally Released Under LGPL - original licence link has changed is not relivant.
32679  *
32680  * Fork - LGPL
32681  * <script type="text/javascript">
32682  */
32683  
32684 /**
32685  * @class Roo.bootstrap.PagingToolbar
32686  * @extends Roo.bootstrap.nav.Simplebar
32687  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32688  * @constructor
32689  * Create a new PagingToolbar
32690  * @param {Object} config The config object
32691  * @param {Roo.data.Store} store
32692  */
32693 Roo.bootstrap.PagingToolbar = function(config)
32694 {
32695     // old args format still supported... - xtype is prefered..
32696         // created from xtype...
32697     
32698     this.ds = config.dataSource;
32699     
32700     if (config.store && !this.ds) {
32701         this.store= Roo.factory(config.store, Roo.data);
32702         this.ds = this.store;
32703         this.ds.xmodule = this.xmodule || false;
32704     }
32705     
32706     this.toolbarItems = [];
32707     if (config.items) {
32708         this.toolbarItems = config.items;
32709     }
32710     
32711     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
32712     
32713     this.cursor = 0;
32714     
32715     if (this.ds) { 
32716         this.bind(this.ds);
32717     }
32718     
32719     if (Roo.bootstrap.version == 4) {
32720         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
32721     } else {
32722         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
32723     }
32724     
32725 };
32726
32727 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
32728     /**
32729      * @cfg {Roo.bootstrap.Button} buttons[]
32730      * Buttons for the toolbar
32731      */
32732      /**
32733      * @cfg {Roo.data.Store} store
32734      * The underlying data store providing the paged data
32735      */
32736     /**
32737      * @cfg {String/HTMLElement/Element} container
32738      * container The id or element that will contain the toolbar
32739      */
32740     /**
32741      * @cfg {Boolean} displayInfo
32742      * True to display the displayMsg (defaults to false)
32743      */
32744     /**
32745      * @cfg {Number} pageSize
32746      * The number of records to display per page (defaults to 20)
32747      */
32748     pageSize: 20,
32749     /**
32750      * @cfg {String} displayMsg
32751      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32752      */
32753     displayMsg : 'Displaying {0} - {1} of {2}',
32754     /**
32755      * @cfg {String} emptyMsg
32756      * The message to display when no records are found (defaults to "No data to display")
32757      */
32758     emptyMsg : 'No data to display',
32759     /**
32760      * Customizable piece of the default paging text (defaults to "Page")
32761      * @type String
32762      */
32763     beforePageText : "Page",
32764     /**
32765      * Customizable piece of the default paging text (defaults to "of %0")
32766      * @type String
32767      */
32768     afterPageText : "of {0}",
32769     /**
32770      * Customizable piece of the default paging text (defaults to "First Page")
32771      * @type String
32772      */
32773     firstText : "First Page",
32774     /**
32775      * Customizable piece of the default paging text (defaults to "Previous Page")
32776      * @type String
32777      */
32778     prevText : "Previous Page",
32779     /**
32780      * Customizable piece of the default paging text (defaults to "Next Page")
32781      * @type String
32782      */
32783     nextText : "Next Page",
32784     /**
32785      * Customizable piece of the default paging text (defaults to "Last Page")
32786      * @type String
32787      */
32788     lastText : "Last Page",
32789     /**
32790      * Customizable piece of the default paging text (defaults to "Refresh")
32791      * @type String
32792      */
32793     refreshText : "Refresh",
32794
32795     buttons : false,
32796     // private
32797     onRender : function(ct, position) 
32798     {
32799         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
32800         this.navgroup.parentId = this.id;
32801         this.navgroup.onRender(this.el, null);
32802         // add the buttons to the navgroup
32803         
32804         if(this.displayInfo){
32805             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
32806             this.displayEl = this.el.select('.x-paging-info', true).first();
32807 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
32808 //            this.displayEl = navel.el.select('span',true).first();
32809         }
32810         
32811         var _this = this;
32812         
32813         if(this.buttons){
32814             Roo.each(_this.buttons, function(e){ // this might need to use render????
32815                Roo.factory(e).render(_this.el);
32816             });
32817         }
32818             
32819         Roo.each(_this.toolbarItems, function(e) {
32820             _this.navgroup.addItem(e);
32821         });
32822         
32823         
32824         this.first = this.navgroup.addItem({
32825             tooltip: this.firstText,
32826             cls: "prev btn-outline-secondary",
32827             html : ' <i class="fa fa-step-backward"></i>',
32828             disabled: true,
32829             preventDefault: true,
32830             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
32831         });
32832         
32833         this.prev =  this.navgroup.addItem({
32834             tooltip: this.prevText,
32835             cls: "prev btn-outline-secondary",
32836             html : ' <i class="fa fa-backward"></i>',
32837             disabled: true,
32838             preventDefault: true,
32839             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
32840         });
32841     //this.addSeparator();
32842         
32843         
32844         var field = this.navgroup.addItem( {
32845             tagtype : 'span',
32846             cls : 'x-paging-position  btn-outline-secondary',
32847              disabled: true,
32848             html : this.beforePageText  +
32849                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
32850                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
32851          } ); //?? escaped?
32852         
32853         this.field = field.el.select('input', true).first();
32854         this.field.on("keydown", this.onPagingKeydown, this);
32855         this.field.on("focus", function(){this.dom.select();});
32856     
32857     
32858         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
32859         //this.field.setHeight(18);
32860         //this.addSeparator();
32861         this.next = this.navgroup.addItem({
32862             tooltip: this.nextText,
32863             cls: "next btn-outline-secondary",
32864             html : ' <i class="fa fa-forward"></i>',
32865             disabled: true,
32866             preventDefault: true,
32867             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
32868         });
32869         this.last = this.navgroup.addItem({
32870             tooltip: this.lastText,
32871             html : ' <i class="fa fa-step-forward"></i>',
32872             cls: "next btn-outline-secondary",
32873             disabled: true,
32874             preventDefault: true,
32875             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
32876         });
32877     //this.addSeparator();
32878         this.loading = this.navgroup.addItem({
32879             tooltip: this.refreshText,
32880             cls: "btn-outline-secondary",
32881             html : ' <i class="fa fa-refresh"></i>',
32882             preventDefault: true,
32883             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
32884         });
32885         
32886     },
32887
32888     // private
32889     updateInfo : function(){
32890         if(this.displayEl){
32891             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
32892             var msg = count == 0 ?
32893                 this.emptyMsg :
32894                 String.format(
32895                     this.displayMsg,
32896                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32897                 );
32898             this.displayEl.update(msg);
32899         }
32900     },
32901
32902     // private
32903     onLoad : function(ds, r, o)
32904     {
32905         this.cursor = o.params && o.params.start ? o.params.start : 0;
32906         
32907         var d = this.getPageData(),
32908             ap = d.activePage,
32909             ps = d.pages;
32910         
32911         
32912         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
32913         this.field.dom.value = ap;
32914         this.first.setDisabled(ap == 1);
32915         this.prev.setDisabled(ap == 1);
32916         this.next.setDisabled(ap == ps);
32917         this.last.setDisabled(ap == ps);
32918         this.loading.enable();
32919         this.updateInfo();
32920     },
32921
32922     // private
32923     getPageData : function(){
32924         var total = this.ds.getTotalCount();
32925         return {
32926             total : total,
32927             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32928             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32929         };
32930     },
32931
32932     // private
32933     onLoadError : function(proxy, o){
32934         this.loading.enable();
32935         if (this.ds.events.loadexception.listeners.length  < 2) {
32936             // nothing has been assigned to loadexception except this...
32937             // so 
32938             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
32939
32940         }
32941     },
32942
32943     // private
32944     onPagingKeydown : function(e){
32945         var k = e.getKey();
32946         var d = this.getPageData();
32947         if(k == e.RETURN){
32948             var v = this.field.dom.value, pageNum;
32949             if(!v || isNaN(pageNum = parseInt(v, 10))){
32950                 this.field.dom.value = d.activePage;
32951                 return;
32952             }
32953             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32954             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32955             e.stopEvent();
32956         }
32957         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))
32958         {
32959           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32960           this.field.dom.value = pageNum;
32961           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32962           e.stopEvent();
32963         }
32964         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32965         {
32966           var v = this.field.dom.value, pageNum; 
32967           var increment = (e.shiftKey) ? 10 : 1;
32968           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32969                 increment *= -1;
32970           }
32971           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32972             this.field.dom.value = d.activePage;
32973             return;
32974           }
32975           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32976           {
32977             this.field.dom.value = parseInt(v, 10) + increment;
32978             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32979             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32980           }
32981           e.stopEvent();
32982         }
32983     },
32984
32985     // private
32986     beforeLoad : function(){
32987         if(this.loading){
32988             this.loading.disable();
32989         }
32990     },
32991
32992     // private
32993     onClick : function(which){
32994         
32995         var ds = this.ds;
32996         if (!ds) {
32997             return;
32998         }
32999         
33000         switch(which){
33001             case "first":
33002                 ds.load({params:{start: 0, limit: this.pageSize}});
33003             break;
33004             case "prev":
33005                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33006             break;
33007             case "next":
33008                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33009             break;
33010             case "last":
33011                 var total = ds.getTotalCount();
33012                 var extra = total % this.pageSize;
33013                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33014                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33015             break;
33016             case "refresh":
33017                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33018             break;
33019         }
33020     },
33021
33022     /**
33023      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33024      * @param {Roo.data.Store} store The data store to unbind
33025      */
33026     unbind : function(ds){
33027         ds.un("beforeload", this.beforeLoad, this);
33028         ds.un("load", this.onLoad, this);
33029         ds.un("loadexception", this.onLoadError, this);
33030         ds.un("remove", this.updateInfo, this);
33031         ds.un("add", this.updateInfo, this);
33032         this.ds = undefined;
33033     },
33034
33035     /**
33036      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33037      * @param {Roo.data.Store} store The data store to bind
33038      */
33039     bind : function(ds){
33040         ds.on("beforeload", this.beforeLoad, this);
33041         ds.on("load", this.onLoad, this);
33042         ds.on("loadexception", this.onLoadError, this);
33043         ds.on("remove", this.updateInfo, this);
33044         ds.on("add", this.updateInfo, this);
33045         this.ds = ds;
33046     }
33047 });/*
33048  * - LGPL
33049  *
33050  * element
33051  * 
33052  */
33053
33054 /**
33055  * @class Roo.bootstrap.MessageBar
33056  * @extends Roo.bootstrap.Component
33057  * Bootstrap MessageBar class
33058  * @cfg {String} html contents of the MessageBar
33059  * @cfg {String} weight (info | success | warning | danger) default info
33060  * @cfg {String} beforeClass insert the bar before the given class
33061  * @cfg {Boolean} closable (true | false) default false
33062  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33063  * 
33064  * @constructor
33065  * Create a new Element
33066  * @param {Object} config The config object
33067  */
33068
33069 Roo.bootstrap.MessageBar = function(config){
33070     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33071 };
33072
33073 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33074     
33075     html: '',
33076     weight: 'info',
33077     closable: false,
33078     fixed: false,
33079     beforeClass: 'bootstrap-sticky-wrap',
33080     
33081     getAutoCreate : function(){
33082         
33083         var cfg = {
33084             tag: 'div',
33085             cls: 'alert alert-dismissable alert-' + this.weight,
33086             cn: [
33087                 {
33088                     tag: 'span',
33089                     cls: 'message',
33090                     html: this.html || ''
33091                 }
33092             ]
33093         };
33094         
33095         if(this.fixed){
33096             cfg.cls += ' alert-messages-fixed';
33097         }
33098         
33099         if(this.closable){
33100             cfg.cn.push({
33101                 tag: 'button',
33102                 cls: 'close',
33103                 html: 'x'
33104             });
33105         }
33106         
33107         return cfg;
33108     },
33109     
33110     onRender : function(ct, position)
33111     {
33112         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33113         
33114         if(!this.el){
33115             var cfg = Roo.apply({},  this.getAutoCreate());
33116             cfg.id = Roo.id();
33117             
33118             if (this.cls) {
33119                 cfg.cls += ' ' + this.cls;
33120             }
33121             if (this.style) {
33122                 cfg.style = this.style;
33123             }
33124             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33125             
33126             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33127         }
33128         
33129         this.el.select('>button.close').on('click', this.hide, this);
33130         
33131     },
33132     
33133     show : function()
33134     {
33135         if (!this.rendered) {
33136             this.render();
33137         }
33138         
33139         this.el.show();
33140         
33141         this.fireEvent('show', this);
33142         
33143     },
33144     
33145     hide : function()
33146     {
33147         if (!this.rendered) {
33148             this.render();
33149         }
33150         
33151         this.el.hide();
33152         
33153         this.fireEvent('hide', this);
33154     },
33155     
33156     update : function()
33157     {
33158 //        var e = this.el.dom.firstChild;
33159 //        
33160 //        if(this.closable){
33161 //            e = e.nextSibling;
33162 //        }
33163 //        
33164 //        e.data = this.html || '';
33165
33166         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33167     }
33168    
33169 });
33170
33171  
33172
33173      /*
33174  * - LGPL
33175  *
33176  * Graph
33177  * 
33178  */
33179
33180
33181 /**
33182  * @class Roo.bootstrap.Graph
33183  * @extends Roo.bootstrap.Component
33184  * Bootstrap Graph class
33185 > Prameters
33186  -sm {number} sm 4
33187  -md {number} md 5
33188  @cfg {String} graphtype  bar | vbar | pie
33189  @cfg {number} g_x coodinator | centre x (pie)
33190  @cfg {number} g_y coodinator | centre y (pie)
33191  @cfg {number} g_r radius (pie)
33192  @cfg {number} g_height height of the chart (respected by all elements in the set)
33193  @cfg {number} g_width width of the chart (respected by all elements in the set)
33194  @cfg {Object} title The title of the chart
33195     
33196  -{Array}  values
33197  -opts (object) options for the chart 
33198      o {
33199      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33200      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33201      o vgutter (number)
33202      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.
33203      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33204      o to
33205      o stretch (boolean)
33206      o }
33207  -opts (object) options for the pie
33208      o{
33209      o cut
33210      o startAngle (number)
33211      o endAngle (number)
33212      } 
33213  *
33214  * @constructor
33215  * Create a new Input
33216  * @param {Object} config The config object
33217  */
33218
33219 Roo.bootstrap.Graph = function(config){
33220     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33221     
33222     this.addEvents({
33223         // img events
33224         /**
33225          * @event click
33226          * The img click event for the img.
33227          * @param {Roo.EventObject} e
33228          */
33229         "click" : true
33230     });
33231 };
33232
33233 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33234     
33235     sm: 4,
33236     md: 5,
33237     graphtype: 'bar',
33238     g_height: 250,
33239     g_width: 400,
33240     g_x: 50,
33241     g_y: 50,
33242     g_r: 30,
33243     opts:{
33244         //g_colors: this.colors,
33245         g_type: 'soft',
33246         g_gutter: '20%'
33247
33248     },
33249     title : false,
33250
33251     getAutoCreate : function(){
33252         
33253         var cfg = {
33254             tag: 'div',
33255             html : null
33256         };
33257         
33258         
33259         return  cfg;
33260     },
33261
33262     onRender : function(ct,position){
33263         
33264         
33265         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33266         
33267         if (typeof(Raphael) == 'undefined') {
33268             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33269             return;
33270         }
33271         
33272         this.raphael = Raphael(this.el.dom);
33273         
33274                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33275                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33276                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33277                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33278                 /*
33279                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33280                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33281                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33282                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33283                 
33284                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33285                 r.barchart(330, 10, 300, 220, data1);
33286                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33287                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33288                 */
33289                 
33290                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33291                 // r.barchart(30, 30, 560, 250,  xdata, {
33292                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33293                 //     axis : "0 0 1 1",
33294                 //     axisxlabels :  xdata
33295                 //     //yvalues : cols,
33296                    
33297                 // });
33298 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33299 //        
33300 //        this.load(null,xdata,{
33301 //                axis : "0 0 1 1",
33302 //                axisxlabels :  xdata
33303 //                });
33304
33305     },
33306
33307     load : function(graphtype,xdata,opts)
33308     {
33309         this.raphael.clear();
33310         if(!graphtype) {
33311             graphtype = this.graphtype;
33312         }
33313         if(!opts){
33314             opts = this.opts;
33315         }
33316         var r = this.raphael,
33317             fin = function () {
33318                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33319             },
33320             fout = function () {
33321                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33322             },
33323             pfin = function() {
33324                 this.sector.stop();
33325                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33326
33327                 if (this.label) {
33328                     this.label[0].stop();
33329                     this.label[0].attr({ r: 7.5 });
33330                     this.label[1].attr({ "font-weight": 800 });
33331                 }
33332             },
33333             pfout = function() {
33334                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33335
33336                 if (this.label) {
33337                     this.label[0].animate({ r: 5 }, 500, "bounce");
33338                     this.label[1].attr({ "font-weight": 400 });
33339                 }
33340             };
33341
33342         switch(graphtype){
33343             case 'bar':
33344                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33345                 break;
33346             case 'hbar':
33347                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33348                 break;
33349             case 'pie':
33350 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33351 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33352 //            
33353                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33354                 
33355                 break;
33356
33357         }
33358         
33359         if(this.title){
33360             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33361         }
33362         
33363     },
33364     
33365     setTitle: function(o)
33366     {
33367         this.title = o;
33368     },
33369     
33370     initEvents: function() {
33371         
33372         if(!this.href){
33373             this.el.on('click', this.onClick, this);
33374         }
33375     },
33376     
33377     onClick : function(e)
33378     {
33379         Roo.log('img onclick');
33380         this.fireEvent('click', this, e);
33381     }
33382    
33383 });
33384
33385  
33386 Roo.bootstrap.dash = {};/*
33387  * - LGPL
33388  *
33389  * numberBox
33390  * 
33391  */
33392 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33393
33394 /**
33395  * @class Roo.bootstrap.dash.NumberBox
33396  * @extends Roo.bootstrap.Component
33397  * Bootstrap NumberBox class
33398  * @cfg {String} headline Box headline
33399  * @cfg {String} content Box content
33400  * @cfg {String} icon Box icon
33401  * @cfg {String} footer Footer text
33402  * @cfg {String} fhref Footer href
33403  * 
33404  * @constructor
33405  * Create a new NumberBox
33406  * @param {Object} config The config object
33407  */
33408
33409
33410 Roo.bootstrap.dash.NumberBox = function(config){
33411     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33412     
33413 };
33414
33415 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33416     
33417     headline : '',
33418     content : '',
33419     icon : '',
33420     footer : '',
33421     fhref : '',
33422     ficon : '',
33423     
33424     getAutoCreate : function(){
33425         
33426         var cfg = {
33427             tag : 'div',
33428             cls : 'small-box ',
33429             cn : [
33430                 {
33431                     tag : 'div',
33432                     cls : 'inner',
33433                     cn :[
33434                         {
33435                             tag : 'h3',
33436                             cls : 'roo-headline',
33437                             html : this.headline
33438                         },
33439                         {
33440                             tag : 'p',
33441                             cls : 'roo-content',
33442                             html : this.content
33443                         }
33444                     ]
33445                 }
33446             ]
33447         };
33448         
33449         if(this.icon){
33450             cfg.cn.push({
33451                 tag : 'div',
33452                 cls : 'icon',
33453                 cn :[
33454                     {
33455                         tag : 'i',
33456                         cls : 'ion ' + this.icon
33457                     }
33458                 ]
33459             });
33460         }
33461         
33462         if(this.footer){
33463             var footer = {
33464                 tag : 'a',
33465                 cls : 'small-box-footer',
33466                 href : this.fhref || '#',
33467                 html : this.footer
33468             };
33469             
33470             cfg.cn.push(footer);
33471             
33472         }
33473         
33474         return  cfg;
33475     },
33476
33477     onRender : function(ct,position){
33478         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33479
33480
33481        
33482                 
33483     },
33484
33485     setHeadline: function (value)
33486     {
33487         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33488     },
33489     
33490     setFooter: function (value, href)
33491     {
33492         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33493         
33494         if(href){
33495             this.el.select('a.small-box-footer',true).first().attr('href', href);
33496         }
33497         
33498     },
33499
33500     setContent: function (value)
33501     {
33502         this.el.select('.roo-content',true).first().dom.innerHTML = value;
33503     },
33504
33505     initEvents: function() 
33506     {   
33507         
33508     }
33509     
33510 });
33511
33512  
33513 /*
33514  * - LGPL
33515  *
33516  * TabBox
33517  * 
33518  */
33519 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33520
33521 /**
33522  * @class Roo.bootstrap.dash.TabBox
33523  * @extends Roo.bootstrap.Component
33524  * @children Roo.bootstrap.dash.TabPane
33525  * Bootstrap TabBox class
33526  * @cfg {String} title Title of the TabBox
33527  * @cfg {String} icon Icon of the TabBox
33528  * @cfg {Boolean} showtabs (true|false) show the tabs default true
33529  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33530  * 
33531  * @constructor
33532  * Create a new TabBox
33533  * @param {Object} config The config object
33534  */
33535
33536
33537 Roo.bootstrap.dash.TabBox = function(config){
33538     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
33539     this.addEvents({
33540         // raw events
33541         /**
33542          * @event addpane
33543          * When a pane is added
33544          * @param {Roo.bootstrap.dash.TabPane} pane
33545          */
33546         "addpane" : true,
33547         /**
33548          * @event activatepane
33549          * When a pane is activated
33550          * @param {Roo.bootstrap.dash.TabPane} pane
33551          */
33552         "activatepane" : true
33553         
33554          
33555     });
33556     
33557     this.panes = [];
33558 };
33559
33560 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
33561
33562     title : '',
33563     icon : false,
33564     showtabs : true,
33565     tabScrollable : false,
33566     
33567     getChildContainer : function()
33568     {
33569         return this.el.select('.tab-content', true).first();
33570     },
33571     
33572     getAutoCreate : function(){
33573         
33574         var header = {
33575             tag: 'li',
33576             cls: 'pull-left header',
33577             html: this.title,
33578             cn : []
33579         };
33580         
33581         if(this.icon){
33582             header.cn.push({
33583                 tag: 'i',
33584                 cls: 'fa ' + this.icon
33585             });
33586         }
33587         
33588         var h = {
33589             tag: 'ul',
33590             cls: 'nav nav-tabs pull-right',
33591             cn: [
33592                 header
33593             ]
33594         };
33595         
33596         if(this.tabScrollable){
33597             h = {
33598                 tag: 'div',
33599                 cls: 'tab-header',
33600                 cn: [
33601                     {
33602                         tag: 'ul',
33603                         cls: 'nav nav-tabs pull-right',
33604                         cn: [
33605                             header
33606                         ]
33607                     }
33608                 ]
33609             };
33610         }
33611         
33612         var cfg = {
33613             tag: 'div',
33614             cls: 'nav-tabs-custom',
33615             cn: [
33616                 h,
33617                 {
33618                     tag: 'div',
33619                     cls: 'tab-content no-padding',
33620                     cn: []
33621                 }
33622             ]
33623         };
33624
33625         return  cfg;
33626     },
33627     initEvents : function()
33628     {
33629         //Roo.log('add add pane handler');
33630         this.on('addpane', this.onAddPane, this);
33631     },
33632      /**
33633      * Updates the box title
33634      * @param {String} html to set the title to.
33635      */
33636     setTitle : function(value)
33637     {
33638         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
33639     },
33640     onAddPane : function(pane)
33641     {
33642         this.panes.push(pane);
33643         //Roo.log('addpane');
33644         //Roo.log(pane);
33645         // tabs are rendere left to right..
33646         if(!this.showtabs){
33647             return;
33648         }
33649         
33650         var ctr = this.el.select('.nav-tabs', true).first();
33651          
33652          
33653         var existing = ctr.select('.nav-tab',true);
33654         var qty = existing.getCount();;
33655         
33656         
33657         var tab = ctr.createChild({
33658             tag : 'li',
33659             cls : 'nav-tab' + (qty ? '' : ' active'),
33660             cn : [
33661                 {
33662                     tag : 'a',
33663                     href:'#',
33664                     html : pane.title
33665                 }
33666             ]
33667         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
33668         pane.tab = tab;
33669         
33670         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
33671         if (!qty) {
33672             pane.el.addClass('active');
33673         }
33674         
33675                 
33676     },
33677     onTabClick : function(ev,un,ob,pane)
33678     {
33679         //Roo.log('tab - prev default');
33680         ev.preventDefault();
33681         
33682         
33683         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
33684         pane.tab.addClass('active');
33685         //Roo.log(pane.title);
33686         this.getChildContainer().select('.tab-pane',true).removeClass('active');
33687         // technically we should have a deactivate event.. but maybe add later.
33688         // and it should not de-activate the selected tab...
33689         this.fireEvent('activatepane', pane);
33690         pane.el.addClass('active');
33691         pane.fireEvent('activate');
33692         
33693         
33694     },
33695     
33696     getActivePane : function()
33697     {
33698         var r = false;
33699         Roo.each(this.panes, function(p) {
33700             if(p.el.hasClass('active')){
33701                 r = p;
33702                 return false;
33703             }
33704             
33705             return;
33706         });
33707         
33708         return r;
33709     }
33710     
33711     
33712 });
33713
33714  
33715 /*
33716  * - LGPL
33717  *
33718  * Tab pane
33719  * 
33720  */
33721 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33722 /**
33723  * @class Roo.bootstrap.TabPane
33724  * @extends Roo.bootstrap.Component
33725  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
33726  * Bootstrap TabPane class
33727  * @cfg {Boolean} active (false | true) Default false
33728  * @cfg {String} title title of panel
33729
33730  * 
33731  * @constructor
33732  * Create a new TabPane
33733  * @param {Object} config The config object
33734  */
33735
33736 Roo.bootstrap.dash.TabPane = function(config){
33737     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
33738     
33739     this.addEvents({
33740         // raw events
33741         /**
33742          * @event activate
33743          * When a pane is activated
33744          * @param {Roo.bootstrap.dash.TabPane} pane
33745          */
33746         "activate" : true
33747          
33748     });
33749 };
33750
33751 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
33752     
33753     active : false,
33754     title : '',
33755     
33756     // the tabBox that this is attached to.
33757     tab : false,
33758      
33759     getAutoCreate : function() 
33760     {
33761         var cfg = {
33762             tag: 'div',
33763             cls: 'tab-pane'
33764         };
33765         
33766         if(this.active){
33767             cfg.cls += ' active';
33768         }
33769         
33770         return cfg;
33771     },
33772     initEvents  : function()
33773     {
33774         //Roo.log('trigger add pane handler');
33775         this.parent().fireEvent('addpane', this)
33776     },
33777     
33778      /**
33779      * Updates the tab title 
33780      * @param {String} html to set the title to.
33781      */
33782     setTitle: function(str)
33783     {
33784         if (!this.tab) {
33785             return;
33786         }
33787         this.title = str;
33788         this.tab.select('a', true).first().dom.innerHTML = str;
33789         
33790     }
33791     
33792     
33793     
33794 });
33795
33796  
33797
33798
33799  /*
33800  * - LGPL
33801  *
33802  * Tooltip
33803  * 
33804  */
33805
33806 /**
33807  * @class Roo.bootstrap.Tooltip
33808  * Bootstrap Tooltip class
33809  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
33810  * to determine which dom element triggers the tooltip.
33811  * 
33812  * It needs to add support for additional attributes like tooltip-position
33813  * 
33814  * @constructor
33815  * Create a new Toolti
33816  * @param {Object} config The config object
33817  */
33818
33819 Roo.bootstrap.Tooltip = function(config){
33820     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
33821     
33822     this.alignment = Roo.bootstrap.Tooltip.alignment;
33823     
33824     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
33825         this.alignment = config.alignment;
33826     }
33827     
33828 };
33829
33830 Roo.apply(Roo.bootstrap.Tooltip, {
33831     /**
33832      * @function init initialize tooltip monitoring.
33833      * @static
33834      */
33835     currentEl : false,
33836     currentTip : false,
33837     currentRegion : false,
33838     
33839     //  init : delay?
33840     
33841     init : function()
33842     {
33843         Roo.get(document).on('mouseover', this.enter ,this);
33844         Roo.get(document).on('mouseout', this.leave, this);
33845          
33846         
33847         this.currentTip = new Roo.bootstrap.Tooltip();
33848     },
33849     
33850     enter : function(ev)
33851     {
33852         var dom = ev.getTarget();
33853         
33854         //Roo.log(['enter',dom]);
33855         var el = Roo.fly(dom);
33856         if (this.currentEl) {
33857             //Roo.log(dom);
33858             //Roo.log(this.currentEl);
33859             //Roo.log(this.currentEl.contains(dom));
33860             if (this.currentEl == el) {
33861                 return;
33862             }
33863             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
33864                 return;
33865             }
33866
33867         }
33868         
33869         if (this.currentTip.el) {
33870             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
33871         }    
33872         //Roo.log(ev);
33873         
33874         if(!el || el.dom == document){
33875             return;
33876         }
33877         
33878         var bindEl = el; 
33879         var pel = false;
33880         if (!el.attr('tooltip')) {
33881             pel = el.findParent("[tooltip]");
33882             if (pel) {
33883                 bindEl = Roo.get(pel);
33884             }
33885         }
33886         
33887        
33888         
33889         // you can not look for children, as if el is the body.. then everythign is the child..
33890         if (!pel && !el.attr('tooltip')) { //
33891             if (!el.select("[tooltip]").elements.length) {
33892                 return;
33893             }
33894             // is the mouse over this child...?
33895             bindEl = el.select("[tooltip]").first();
33896             var xy = ev.getXY();
33897             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
33898                 //Roo.log("not in region.");
33899                 return;
33900             }
33901             //Roo.log("child element over..");
33902             
33903         }
33904         this.currentEl = el;
33905         this.currentTip.bind(bindEl);
33906         this.currentRegion = Roo.lib.Region.getRegion(dom);
33907         this.currentTip.enter();
33908         
33909     },
33910     leave : function(ev)
33911     {
33912         var dom = ev.getTarget();
33913         //Roo.log(['leave',dom]);
33914         if (!this.currentEl) {
33915             return;
33916         }
33917         
33918         
33919         if (dom != this.currentEl.dom) {
33920             return;
33921         }
33922         var xy = ev.getXY();
33923         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
33924             return;
33925         }
33926         // only activate leave if mouse cursor is outside... bounding box..
33927         
33928         
33929         
33930         
33931         if (this.currentTip) {
33932             this.currentTip.leave();
33933         }
33934         //Roo.log('clear currentEl');
33935         this.currentEl = false;
33936         
33937         
33938     },
33939     alignment : {
33940         'left' : ['r-l', [-2,0], 'right'],
33941         'right' : ['l-r', [2,0], 'left'],
33942         'bottom' : ['t-b', [0,2], 'top'],
33943         'top' : [ 'b-t', [0,-2], 'bottom']
33944     }
33945     
33946 });
33947
33948
33949 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
33950     
33951     
33952     bindEl : false,
33953     
33954     delay : null, // can be { show : 300 , hide: 500}
33955     
33956     timeout : null,
33957     
33958     hoverState : null, //???
33959     
33960     placement : 'bottom', 
33961     
33962     alignment : false,
33963     
33964     getAutoCreate : function(){
33965     
33966         var cfg = {
33967            cls : 'tooltip',   
33968            role : 'tooltip',
33969            cn : [
33970                 {
33971                     cls : 'tooltip-arrow arrow'
33972                 },
33973                 {
33974                     cls : 'tooltip-inner'
33975                 }
33976            ]
33977         };
33978         
33979         return cfg;
33980     },
33981     bind : function(el)
33982     {
33983         this.bindEl = el;
33984     },
33985     
33986     initEvents : function()
33987     {
33988         this.arrowEl = this.el.select('.arrow', true).first();
33989         this.innerEl = this.el.select('.tooltip-inner', true).first();
33990     },
33991     
33992     enter : function () {
33993        
33994         if (this.timeout != null) {
33995             clearTimeout(this.timeout);
33996         }
33997         
33998         this.hoverState = 'in';
33999          //Roo.log("enter - show");
34000         if (!this.delay || !this.delay.show) {
34001             this.show();
34002             return;
34003         }
34004         var _t = this;
34005         this.timeout = setTimeout(function () {
34006             if (_t.hoverState == 'in') {
34007                 _t.show();
34008             }
34009         }, this.delay.show);
34010     },
34011     leave : function()
34012     {
34013         clearTimeout(this.timeout);
34014     
34015         this.hoverState = 'out';
34016          if (!this.delay || !this.delay.hide) {
34017             this.hide();
34018             return;
34019         }
34020        
34021         var _t = this;
34022         this.timeout = setTimeout(function () {
34023             //Roo.log("leave - timeout");
34024             
34025             if (_t.hoverState == 'out') {
34026                 _t.hide();
34027                 Roo.bootstrap.Tooltip.currentEl = false;
34028             }
34029         }, delay);
34030     },
34031     
34032     show : function (msg)
34033     {
34034         if (!this.el) {
34035             this.render(document.body);
34036         }
34037         // set content.
34038         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34039         
34040         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34041         
34042         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34043         
34044         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34045                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34046
34047         if(this.bindEl.attr('tooltip-class')) {
34048             this.el.addClass(this.bindEl.attr('tooltip-class'));
34049         }
34050         
34051         var placement = typeof this.placement == 'function' ?
34052             this.placement.call(this, this.el, on_el) :
34053             this.placement;
34054         
34055         if(this.bindEl.attr('tooltip-placement')) {
34056             placement = this.bindEl.attr('tooltip-placement');
34057         }
34058             
34059         var autoToken = /\s?auto?\s?/i;
34060         var autoPlace = autoToken.test(placement);
34061         if (autoPlace) {
34062             placement = placement.replace(autoToken, '') || 'top';
34063         }
34064         
34065         //this.el.detach()
34066         //this.el.setXY([0,0]);
34067         this.el.show();
34068         //this.el.dom.style.display='block';
34069         
34070         //this.el.appendTo(on_el);
34071         
34072         var p = this.getPosition();
34073         var box = this.el.getBox();
34074         
34075         if (autoPlace) {
34076             // fixme..
34077         }
34078         
34079         var align = this.alignment[placement];
34080         
34081         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34082         
34083         if(placement == 'top' || placement == 'bottom'){
34084             if(xy[0] < 0){
34085                 placement = 'right';
34086             }
34087             
34088             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34089                 placement = 'left';
34090             }
34091             
34092             var scroll = Roo.select('body', true).first().getScroll();
34093             
34094             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34095                 placement = 'top';
34096             }
34097             
34098             align = this.alignment[placement];
34099             
34100             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34101             
34102         }
34103         
34104         var elems = document.getElementsByTagName('div');
34105         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34106         for (var i = 0; i < elems.length; i++) {
34107           var zindex = Number.parseInt(
34108                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34109                 10
34110           );
34111           if (zindex > highest) {
34112             highest = zindex;
34113           }
34114         }
34115         
34116         
34117         
34118         this.el.dom.style.zIndex = highest;
34119         
34120         this.el.alignTo(this.bindEl, align[0],align[1]);
34121         //var arrow = this.el.select('.arrow',true).first();
34122         //arrow.set(align[2], 
34123         
34124         this.el.addClass(placement);
34125         this.el.addClass("bs-tooltip-"+ placement);
34126         
34127         this.el.addClass('in fade show');
34128         
34129         this.hoverState = null;
34130         
34131         if (this.el.hasClass('fade')) {
34132             // fade it?
34133         }
34134         
34135         
34136         
34137         
34138         
34139     },
34140     hide : function()
34141     {
34142          
34143         if (!this.el) {
34144             return;
34145         }
34146         //this.el.setXY([0,0]);
34147         if(this.bindEl.attr('tooltip-class')) {
34148             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34149         }
34150         this.el.removeClass(['show', 'in']);
34151         //this.el.hide();
34152         
34153     }
34154     
34155 });
34156  
34157
34158  /*
34159  * - LGPL
34160  *
34161  * Location Picker
34162  * 
34163  */
34164
34165 /**
34166  * @class Roo.bootstrap.LocationPicker
34167  * @extends Roo.bootstrap.Component
34168  * Bootstrap LocationPicker class
34169  * @cfg {Number} latitude Position when init default 0
34170  * @cfg {Number} longitude Position when init default 0
34171  * @cfg {Number} zoom default 15
34172  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34173  * @cfg {Boolean} mapTypeControl default false
34174  * @cfg {Boolean} disableDoubleClickZoom default false
34175  * @cfg {Boolean} scrollwheel default true
34176  * @cfg {Boolean} streetViewControl default false
34177  * @cfg {Number} radius default 0
34178  * @cfg {String} locationName
34179  * @cfg {Boolean} draggable default true
34180  * @cfg {Boolean} enableAutocomplete default false
34181  * @cfg {Boolean} enableReverseGeocode default true
34182  * @cfg {String} markerTitle
34183  * 
34184  * @constructor
34185  * Create a new LocationPicker
34186  * @param {Object} config The config object
34187  */
34188
34189
34190 Roo.bootstrap.LocationPicker = function(config){
34191     
34192     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34193     
34194     this.addEvents({
34195         /**
34196          * @event initial
34197          * Fires when the picker initialized.
34198          * @param {Roo.bootstrap.LocationPicker} this
34199          * @param {Google Location} location
34200          */
34201         initial : true,
34202         /**
34203          * @event positionchanged
34204          * Fires when the picker position changed.
34205          * @param {Roo.bootstrap.LocationPicker} this
34206          * @param {Google Location} location
34207          */
34208         positionchanged : true,
34209         /**
34210          * @event resize
34211          * Fires when the map resize.
34212          * @param {Roo.bootstrap.LocationPicker} this
34213          */
34214         resize : true,
34215         /**
34216          * @event show
34217          * Fires when the map show.
34218          * @param {Roo.bootstrap.LocationPicker} this
34219          */
34220         show : true,
34221         /**
34222          * @event hide
34223          * Fires when the map hide.
34224          * @param {Roo.bootstrap.LocationPicker} this
34225          */
34226         hide : true,
34227         /**
34228          * @event mapClick
34229          * Fires when click the map.
34230          * @param {Roo.bootstrap.LocationPicker} this
34231          * @param {Map event} e
34232          */
34233         mapClick : true,
34234         /**
34235          * @event mapRightClick
34236          * Fires when right click the map.
34237          * @param {Roo.bootstrap.LocationPicker} this
34238          * @param {Map event} e
34239          */
34240         mapRightClick : true,
34241         /**
34242          * @event markerClick
34243          * Fires when click the marker.
34244          * @param {Roo.bootstrap.LocationPicker} this
34245          * @param {Map event} e
34246          */
34247         markerClick : true,
34248         /**
34249          * @event markerRightClick
34250          * Fires when right click the marker.
34251          * @param {Roo.bootstrap.LocationPicker} this
34252          * @param {Map event} e
34253          */
34254         markerRightClick : true,
34255         /**
34256          * @event OverlayViewDraw
34257          * Fires when OverlayView Draw
34258          * @param {Roo.bootstrap.LocationPicker} this
34259          */
34260         OverlayViewDraw : true,
34261         /**
34262          * @event OverlayViewOnAdd
34263          * Fires when OverlayView Draw
34264          * @param {Roo.bootstrap.LocationPicker} this
34265          */
34266         OverlayViewOnAdd : true,
34267         /**
34268          * @event OverlayViewOnRemove
34269          * Fires when OverlayView Draw
34270          * @param {Roo.bootstrap.LocationPicker} this
34271          */
34272         OverlayViewOnRemove : true,
34273         /**
34274          * @event OverlayViewShow
34275          * Fires when OverlayView Draw
34276          * @param {Roo.bootstrap.LocationPicker} this
34277          * @param {Pixel} cpx
34278          */
34279         OverlayViewShow : true,
34280         /**
34281          * @event OverlayViewHide
34282          * Fires when OverlayView Draw
34283          * @param {Roo.bootstrap.LocationPicker} this
34284          */
34285         OverlayViewHide : true,
34286         /**
34287          * @event loadexception
34288          * Fires when load google lib failed.
34289          * @param {Roo.bootstrap.LocationPicker} this
34290          */
34291         loadexception : true
34292     });
34293         
34294 };
34295
34296 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34297     
34298     gMapContext: false,
34299     
34300     latitude: 0,
34301     longitude: 0,
34302     zoom: 15,
34303     mapTypeId: false,
34304     mapTypeControl: false,
34305     disableDoubleClickZoom: false,
34306     scrollwheel: true,
34307     streetViewControl: false,
34308     radius: 0,
34309     locationName: '',
34310     draggable: true,
34311     enableAutocomplete: false,
34312     enableReverseGeocode: true,
34313     markerTitle: '',
34314     
34315     getAutoCreate: function()
34316     {
34317
34318         var cfg = {
34319             tag: 'div',
34320             cls: 'roo-location-picker'
34321         };
34322         
34323         return cfg
34324     },
34325     
34326     initEvents: function(ct, position)
34327     {       
34328         if(!this.el.getWidth() || this.isApplied()){
34329             return;
34330         }
34331         
34332         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34333         
34334         this.initial();
34335     },
34336     
34337     initial: function()
34338     {
34339         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34340             this.fireEvent('loadexception', this);
34341             return;
34342         }
34343         
34344         if(!this.mapTypeId){
34345             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34346         }
34347         
34348         this.gMapContext = this.GMapContext();
34349         
34350         this.initOverlayView();
34351         
34352         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34353         
34354         var _this = this;
34355                 
34356         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34357             _this.setPosition(_this.gMapContext.marker.position);
34358         });
34359         
34360         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34361             _this.fireEvent('mapClick', this, event);
34362             
34363         });
34364
34365         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34366             _this.fireEvent('mapRightClick', this, event);
34367             
34368         });
34369         
34370         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34371             _this.fireEvent('markerClick', this, event);
34372             
34373         });
34374
34375         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34376             _this.fireEvent('markerRightClick', this, event);
34377             
34378         });
34379         
34380         this.setPosition(this.gMapContext.location);
34381         
34382         this.fireEvent('initial', this, this.gMapContext.location);
34383     },
34384     
34385     initOverlayView: function()
34386     {
34387         var _this = this;
34388         
34389         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34390             
34391             draw: function()
34392             {
34393                 _this.fireEvent('OverlayViewDraw', _this);
34394             },
34395             
34396             onAdd: function()
34397             {
34398                 _this.fireEvent('OverlayViewOnAdd', _this);
34399             },
34400             
34401             onRemove: function()
34402             {
34403                 _this.fireEvent('OverlayViewOnRemove', _this);
34404             },
34405             
34406             show: function(cpx)
34407             {
34408                 _this.fireEvent('OverlayViewShow', _this, cpx);
34409             },
34410             
34411             hide: function()
34412             {
34413                 _this.fireEvent('OverlayViewHide', _this);
34414             }
34415             
34416         });
34417     },
34418     
34419     fromLatLngToContainerPixel: function(event)
34420     {
34421         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34422     },
34423     
34424     isApplied: function() 
34425     {
34426         return this.getGmapContext() == false ? false : true;
34427     },
34428     
34429     getGmapContext: function() 
34430     {
34431         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34432     },
34433     
34434     GMapContext: function() 
34435     {
34436         var position = new google.maps.LatLng(this.latitude, this.longitude);
34437         
34438         var _map = new google.maps.Map(this.el.dom, {
34439             center: position,
34440             zoom: this.zoom,
34441             mapTypeId: this.mapTypeId,
34442             mapTypeControl: this.mapTypeControl,
34443             disableDoubleClickZoom: this.disableDoubleClickZoom,
34444             scrollwheel: this.scrollwheel,
34445             streetViewControl: this.streetViewControl,
34446             locationName: this.locationName,
34447             draggable: this.draggable,
34448             enableAutocomplete: this.enableAutocomplete,
34449             enableReverseGeocode: this.enableReverseGeocode
34450         });
34451         
34452         var _marker = new google.maps.Marker({
34453             position: position,
34454             map: _map,
34455             title: this.markerTitle,
34456             draggable: this.draggable
34457         });
34458         
34459         return {
34460             map: _map,
34461             marker: _marker,
34462             circle: null,
34463             location: position,
34464             radius: this.radius,
34465             locationName: this.locationName,
34466             addressComponents: {
34467                 formatted_address: null,
34468                 addressLine1: null,
34469                 addressLine2: null,
34470                 streetName: null,
34471                 streetNumber: null,
34472                 city: null,
34473                 district: null,
34474                 state: null,
34475                 stateOrProvince: null
34476             },
34477             settings: this,
34478             domContainer: this.el.dom,
34479             geodecoder: new google.maps.Geocoder()
34480         };
34481     },
34482     
34483     drawCircle: function(center, radius, options) 
34484     {
34485         if (this.gMapContext.circle != null) {
34486             this.gMapContext.circle.setMap(null);
34487         }
34488         if (radius > 0) {
34489             radius *= 1;
34490             options = Roo.apply({}, options, {
34491                 strokeColor: "#0000FF",
34492                 strokeOpacity: .35,
34493                 strokeWeight: 2,
34494                 fillColor: "#0000FF",
34495                 fillOpacity: .2
34496             });
34497             
34498             options.map = this.gMapContext.map;
34499             options.radius = radius;
34500             options.center = center;
34501             this.gMapContext.circle = new google.maps.Circle(options);
34502             return this.gMapContext.circle;
34503         }
34504         
34505         return null;
34506     },
34507     
34508     setPosition: function(location) 
34509     {
34510         this.gMapContext.location = location;
34511         this.gMapContext.marker.setPosition(location);
34512         this.gMapContext.map.panTo(location);
34513         this.drawCircle(location, this.gMapContext.radius, {});
34514         
34515         var _this = this;
34516         
34517         if (this.gMapContext.settings.enableReverseGeocode) {
34518             this.gMapContext.geodecoder.geocode({
34519                 latLng: this.gMapContext.location
34520             }, function(results, status) {
34521                 
34522                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34523                     _this.gMapContext.locationName = results[0].formatted_address;
34524                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34525                     
34526                     _this.fireEvent('positionchanged', this, location);
34527                 }
34528             });
34529             
34530             return;
34531         }
34532         
34533         this.fireEvent('positionchanged', this, location);
34534     },
34535     
34536     resize: function()
34537     {
34538         google.maps.event.trigger(this.gMapContext.map, "resize");
34539         
34540         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
34541         
34542         this.fireEvent('resize', this);
34543     },
34544     
34545     setPositionByLatLng: function(latitude, longitude)
34546     {
34547         this.setPosition(new google.maps.LatLng(latitude, longitude));
34548     },
34549     
34550     getCurrentPosition: function() 
34551     {
34552         return {
34553             latitude: this.gMapContext.location.lat(),
34554             longitude: this.gMapContext.location.lng()
34555         };
34556     },
34557     
34558     getAddressName: function() 
34559     {
34560         return this.gMapContext.locationName;
34561     },
34562     
34563     getAddressComponents: function() 
34564     {
34565         return this.gMapContext.addressComponents;
34566     },
34567     
34568     address_component_from_google_geocode: function(address_components) 
34569     {
34570         var result = {};
34571         
34572         for (var i = 0; i < address_components.length; i++) {
34573             var component = address_components[i];
34574             if (component.types.indexOf("postal_code") >= 0) {
34575                 result.postalCode = component.short_name;
34576             } else if (component.types.indexOf("street_number") >= 0) {
34577                 result.streetNumber = component.short_name;
34578             } else if (component.types.indexOf("route") >= 0) {
34579                 result.streetName = component.short_name;
34580             } else if (component.types.indexOf("neighborhood") >= 0) {
34581                 result.city = component.short_name;
34582             } else if (component.types.indexOf("locality") >= 0) {
34583                 result.city = component.short_name;
34584             } else if (component.types.indexOf("sublocality") >= 0) {
34585                 result.district = component.short_name;
34586             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
34587                 result.stateOrProvince = component.short_name;
34588             } else if (component.types.indexOf("country") >= 0) {
34589                 result.country = component.short_name;
34590             }
34591         }
34592         
34593         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
34594         result.addressLine2 = "";
34595         return result;
34596     },
34597     
34598     setZoomLevel: function(zoom)
34599     {
34600         this.gMapContext.map.setZoom(zoom);
34601     },
34602     
34603     show: function()
34604     {
34605         if(!this.el){
34606             return;
34607         }
34608         
34609         this.el.show();
34610         
34611         this.resize();
34612         
34613         this.fireEvent('show', this);
34614     },
34615     
34616     hide: function()
34617     {
34618         if(!this.el){
34619             return;
34620         }
34621         
34622         this.el.hide();
34623         
34624         this.fireEvent('hide', this);
34625     }
34626     
34627 });
34628
34629 Roo.apply(Roo.bootstrap.LocationPicker, {
34630     
34631     OverlayView : function(map, options)
34632     {
34633         options = options || {};
34634         
34635         this.setMap(map);
34636     }
34637     
34638     
34639 });/**
34640  * @class Roo.bootstrap.Alert
34641  * @extends Roo.bootstrap.Component
34642  * Bootstrap Alert class - shows an alert area box
34643  * eg
34644  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
34645   Enter a valid email address
34646 </div>
34647  * @licence LGPL
34648  * @cfg {String} title The title of alert
34649  * @cfg {String} html The content of alert
34650  * @cfg {String} weight (success|info|warning|danger) Weight of the message
34651  * @cfg {String} fa font-awesomeicon
34652  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
34653  * @cfg {Boolean} close true to show a x closer
34654  * 
34655  * 
34656  * @constructor
34657  * Create a new alert
34658  * @param {Object} config The config object
34659  */
34660
34661
34662 Roo.bootstrap.Alert = function(config){
34663     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
34664     
34665 };
34666
34667 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
34668     
34669     title: '',
34670     html: '',
34671     weight: false,
34672     fa: false,
34673     faicon: false, // BC
34674     close : false,
34675     
34676     
34677     getAutoCreate : function()
34678     {
34679         
34680         var cfg = {
34681             tag : 'div',
34682             cls : 'alert',
34683             cn : [
34684                 {
34685                     tag: 'button',
34686                     type :  "button",
34687                     cls: "close",
34688                     html : '×',
34689                     style : this.close ? '' : 'display:none'
34690                 },
34691                 {
34692                     tag : 'i',
34693                     cls : 'roo-alert-icon'
34694                     
34695                 },
34696                 {
34697                     tag : 'b',
34698                     cls : 'roo-alert-title',
34699                     html : this.title
34700                 },
34701                 {
34702                     tag : 'span',
34703                     cls : 'roo-alert-text',
34704                     html : this.html
34705                 }
34706             ]
34707         };
34708         
34709         if(this.faicon){
34710             cfg.cn[0].cls += ' fa ' + this.faicon;
34711         }
34712         if(this.fa){
34713             cfg.cn[0].cls += ' fa ' + this.fa;
34714         }
34715         
34716         if(this.weight){
34717             cfg.cls += ' alert-' + this.weight;
34718         }
34719         
34720         return cfg;
34721     },
34722     
34723     initEvents: function() 
34724     {
34725         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34726         this.titleEl =  this.el.select('.roo-alert-title',true).first();
34727         this.iconEl = this.el.select('.roo-alert-icon',true).first();
34728         this.htmlEl = this.el.select('.roo-alert-text',true).first();
34729         if (this.seconds > 0) {
34730             this.hide.defer(this.seconds, this);
34731         }
34732     },
34733     /**
34734      * Set the Title Message HTML
34735      * @param {String} html
34736      */
34737     setTitle : function(str)
34738     {
34739         this.titleEl.dom.innerHTML = str;
34740     },
34741      
34742      /**
34743      * Set the Body Message HTML
34744      * @param {String} html
34745      */
34746     setHtml : function(str)
34747     {
34748         this.htmlEl.dom.innerHTML = str;
34749     },
34750     /**
34751      * Set the Weight of the alert
34752      * @param {String} (success|info|warning|danger) weight
34753      */
34754     
34755     setWeight : function(weight)
34756     {
34757         if(this.weight){
34758             this.el.removeClass('alert-' + this.weight);
34759         }
34760         
34761         this.weight = weight;
34762         
34763         this.el.addClass('alert-' + this.weight);
34764     },
34765       /**
34766      * Set the Icon of the alert
34767      * @param {String} see fontawsome names (name without the 'fa-' bit)
34768      */
34769     setIcon : function(icon)
34770     {
34771         if(this.faicon){
34772             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
34773         }
34774         
34775         this.faicon = icon;
34776         
34777         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
34778     },
34779     /**
34780      * Hide the Alert
34781      */
34782     hide: function() 
34783     {
34784         this.el.hide();   
34785     },
34786     /**
34787      * Show the Alert
34788      */
34789     show: function() 
34790     {  
34791         this.el.show();   
34792     }
34793     
34794 });
34795
34796  
34797 /*
34798 * Licence: LGPL
34799 */
34800
34801 /**
34802  * @class Roo.bootstrap.UploadCropbox
34803  * @extends Roo.bootstrap.Component
34804  * Bootstrap UploadCropbox class
34805  * @cfg {String} emptyText show when image has been loaded
34806  * @cfg {String} rotateNotify show when image too small to rotate
34807  * @cfg {Number} errorTimeout default 3000
34808  * @cfg {Number} minWidth default 300
34809  * @cfg {Number} minHeight default 300
34810  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
34811  * @cfg {Boolean} isDocument (true|false) default false
34812  * @cfg {String} url action url
34813  * @cfg {String} paramName default 'imageUpload'
34814  * @cfg {String} method default POST
34815  * @cfg {Boolean} loadMask (true|false) default true
34816  * @cfg {Boolean} loadingText default 'Loading...'
34817  * 
34818  * @constructor
34819  * Create a new UploadCropbox
34820  * @param {Object} config The config object
34821  */
34822
34823 Roo.bootstrap.UploadCropbox = function(config){
34824     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
34825     
34826     this.addEvents({
34827         /**
34828          * @event beforeselectfile
34829          * Fire before select file
34830          * @param {Roo.bootstrap.UploadCropbox} this
34831          */
34832         "beforeselectfile" : true,
34833         /**
34834          * @event initial
34835          * Fire after initEvent
34836          * @param {Roo.bootstrap.UploadCropbox} this
34837          */
34838         "initial" : true,
34839         /**
34840          * @event crop
34841          * Fire after initEvent
34842          * @param {Roo.bootstrap.UploadCropbox} this
34843          * @param {String} data
34844          */
34845         "crop" : true,
34846         /**
34847          * @event prepare
34848          * Fire when preparing the file data
34849          * @param {Roo.bootstrap.UploadCropbox} this
34850          * @param {Object} file
34851          */
34852         "prepare" : true,
34853         /**
34854          * @event exception
34855          * Fire when get exception
34856          * @param {Roo.bootstrap.UploadCropbox} this
34857          * @param {XMLHttpRequest} xhr
34858          */
34859         "exception" : true,
34860         /**
34861          * @event beforeloadcanvas
34862          * Fire before load the canvas
34863          * @param {Roo.bootstrap.UploadCropbox} this
34864          * @param {String} src
34865          */
34866         "beforeloadcanvas" : true,
34867         /**
34868          * @event trash
34869          * Fire when trash image
34870          * @param {Roo.bootstrap.UploadCropbox} this
34871          */
34872         "trash" : true,
34873         /**
34874          * @event download
34875          * Fire when download the image
34876          * @param {Roo.bootstrap.UploadCropbox} this
34877          */
34878         "download" : true,
34879         /**
34880          * @event footerbuttonclick
34881          * Fire when footerbuttonclick
34882          * @param {Roo.bootstrap.UploadCropbox} this
34883          * @param {String} type
34884          */
34885         "footerbuttonclick" : true,
34886         /**
34887          * @event resize
34888          * Fire when resize
34889          * @param {Roo.bootstrap.UploadCropbox} this
34890          */
34891         "resize" : true,
34892         /**
34893          * @event rotate
34894          * Fire when rotate the image
34895          * @param {Roo.bootstrap.UploadCropbox} this
34896          * @param {String} pos
34897          */
34898         "rotate" : true,
34899         /**
34900          * @event inspect
34901          * Fire when inspect the file
34902          * @param {Roo.bootstrap.UploadCropbox} this
34903          * @param {Object} file
34904          */
34905         "inspect" : true,
34906         /**
34907          * @event upload
34908          * Fire when xhr upload the file
34909          * @param {Roo.bootstrap.UploadCropbox} this
34910          * @param {Object} data
34911          */
34912         "upload" : true,
34913         /**
34914          * @event arrange
34915          * Fire when arrange the file data
34916          * @param {Roo.bootstrap.UploadCropbox} this
34917          * @param {Object} formData
34918          */
34919         "arrange" : true
34920     });
34921     
34922     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
34923 };
34924
34925 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
34926     
34927     emptyText : 'Click to upload image',
34928     rotateNotify : 'Image is too small to rotate',
34929     errorTimeout : 3000,
34930     scale : 0,
34931     baseScale : 1,
34932     rotate : 0,
34933     dragable : false,
34934     pinching : false,
34935     mouseX : 0,
34936     mouseY : 0,
34937     cropData : false,
34938     minWidth : 300,
34939     minHeight : 300,
34940     file : false,
34941     exif : {},
34942     baseRotate : 1,
34943     cropType : 'image/jpeg',
34944     buttons : false,
34945     canvasLoaded : false,
34946     isDocument : false,
34947     method : 'POST',
34948     paramName : 'imageUpload',
34949     loadMask : true,
34950     loadingText : 'Loading...',
34951     maskEl : false,
34952     
34953     getAutoCreate : function()
34954     {
34955         var cfg = {
34956             tag : 'div',
34957             cls : 'roo-upload-cropbox',
34958             cn : [
34959                 {
34960                     tag : 'input',
34961                     cls : 'roo-upload-cropbox-selector',
34962                     type : 'file'
34963                 },
34964                 {
34965                     tag : 'div',
34966                     cls : 'roo-upload-cropbox-body',
34967                     style : 'cursor:pointer',
34968                     cn : [
34969                         {
34970                             tag : 'div',
34971                             cls : 'roo-upload-cropbox-preview'
34972                         },
34973                         {
34974                             tag : 'div',
34975                             cls : 'roo-upload-cropbox-thumb'
34976                         },
34977                         {
34978                             tag : 'div',
34979                             cls : 'roo-upload-cropbox-empty-notify',
34980                             html : this.emptyText
34981                         },
34982                         {
34983                             tag : 'div',
34984                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
34985                             html : this.rotateNotify
34986                         }
34987                     ]
34988                 },
34989                 {
34990                     tag : 'div',
34991                     cls : 'roo-upload-cropbox-footer',
34992                     cn : {
34993                         tag : 'div',
34994                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
34995                         cn : []
34996                     }
34997                 }
34998             ]
34999         };
35000         
35001         return cfg;
35002     },
35003     
35004     onRender : function(ct, position)
35005     {
35006         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35007         
35008         if (this.buttons.length) {
35009             
35010             Roo.each(this.buttons, function(bb) {
35011                 
35012                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35013                 
35014                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35015                 
35016             }, this);
35017         }
35018         
35019         if(this.loadMask){
35020             this.maskEl = this.el;
35021         }
35022     },
35023     
35024     initEvents : function()
35025     {
35026         this.urlAPI = (window.createObjectURL && window) || 
35027                                 (window.URL && URL.revokeObjectURL && URL) || 
35028                                 (window.webkitURL && webkitURL);
35029                         
35030         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35031         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35032         
35033         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35034         this.selectorEl.hide();
35035         
35036         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35037         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35038         
35039         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35040         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35041         this.thumbEl.hide();
35042         
35043         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35044         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35045         
35046         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35047         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35048         this.errorEl.hide();
35049         
35050         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35051         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35052         this.footerEl.hide();
35053         
35054         this.setThumbBoxSize();
35055         
35056         this.bind();
35057         
35058         this.resize();
35059         
35060         this.fireEvent('initial', this);
35061     },
35062
35063     bind : function()
35064     {
35065         var _this = this;
35066         
35067         window.addEventListener("resize", function() { _this.resize(); } );
35068         
35069         this.bodyEl.on('click', this.beforeSelectFile, this);
35070         
35071         if(Roo.isTouch){
35072             this.bodyEl.on('touchstart', this.onTouchStart, this);
35073             this.bodyEl.on('touchmove', this.onTouchMove, this);
35074             this.bodyEl.on('touchend', this.onTouchEnd, this);
35075         }
35076         
35077         if(!Roo.isTouch){
35078             this.bodyEl.on('mousedown', this.onMouseDown, this);
35079             this.bodyEl.on('mousemove', this.onMouseMove, this);
35080             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35081             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35082             Roo.get(document).on('mouseup', this.onMouseUp, this);
35083         }
35084         
35085         this.selectorEl.on('change', this.onFileSelected, this);
35086     },
35087     
35088     reset : function()
35089     {    
35090         this.scale = 0;
35091         this.baseScale = 1;
35092         this.rotate = 0;
35093         this.baseRotate = 1;
35094         this.dragable = false;
35095         this.pinching = false;
35096         this.mouseX = 0;
35097         this.mouseY = 0;
35098         this.cropData = false;
35099         this.notifyEl.dom.innerHTML = this.emptyText;
35100         
35101         this.selectorEl.dom.value = '';
35102         
35103     },
35104     
35105     resize : function()
35106     {
35107         if(this.fireEvent('resize', this) != false){
35108             this.setThumbBoxPosition();
35109             this.setCanvasPosition();
35110         }
35111     },
35112     
35113     onFooterButtonClick : function(e, el, o, type)
35114     {
35115         switch (type) {
35116             case 'rotate-left' :
35117                 this.onRotateLeft(e);
35118                 break;
35119             case 'rotate-right' :
35120                 this.onRotateRight(e);
35121                 break;
35122             case 'picture' :
35123                 this.beforeSelectFile(e);
35124                 break;
35125             case 'trash' :
35126                 this.trash(e);
35127                 break;
35128             case 'crop' :
35129                 this.crop(e);
35130                 break;
35131             case 'download' :
35132                 this.download(e);
35133                 break;
35134             default :
35135                 break;
35136         }
35137         
35138         this.fireEvent('footerbuttonclick', this, type);
35139     },
35140     
35141     beforeSelectFile : function(e)
35142     {
35143         e.preventDefault();
35144         
35145         if(this.fireEvent('beforeselectfile', this) != false){
35146             this.selectorEl.dom.click();
35147         }
35148     },
35149     
35150     onFileSelected : function(e)
35151     {
35152         e.preventDefault();
35153         
35154         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35155             return;
35156         }
35157         
35158         var file = this.selectorEl.dom.files[0];
35159         
35160         if(this.fireEvent('inspect', this, file) != false){
35161             this.prepare(file);
35162         }
35163         
35164     },
35165     
35166     trash : function(e)
35167     {
35168         this.fireEvent('trash', this);
35169     },
35170     
35171     download : function(e)
35172     {
35173         this.fireEvent('download', this);
35174     },
35175     
35176     loadCanvas : function(src)
35177     {   
35178         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35179             
35180             this.reset();
35181             
35182             this.imageEl = document.createElement('img');
35183             
35184             var _this = this;
35185             
35186             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35187             
35188             this.imageEl.src = src;
35189         }
35190     },
35191     
35192     onLoadCanvas : function()
35193     {   
35194         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35195         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35196         
35197         this.bodyEl.un('click', this.beforeSelectFile, this);
35198         
35199         this.notifyEl.hide();
35200         this.thumbEl.show();
35201         this.footerEl.show();
35202         
35203         this.baseRotateLevel();
35204         
35205         if(this.isDocument){
35206             this.setThumbBoxSize();
35207         }
35208         
35209         this.setThumbBoxPosition();
35210         
35211         this.baseScaleLevel();
35212         
35213         this.draw();
35214         
35215         this.resize();
35216         
35217         this.canvasLoaded = true;
35218         
35219         if(this.loadMask){
35220             this.maskEl.unmask();
35221         }
35222         
35223     },
35224     
35225     setCanvasPosition : function()
35226     {   
35227         if(!this.canvasEl){
35228             return;
35229         }
35230         
35231         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35232         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35233         
35234         this.previewEl.setLeft(pw);
35235         this.previewEl.setTop(ph);
35236         
35237     },
35238     
35239     onMouseDown : function(e)
35240     {   
35241         e.stopEvent();
35242         
35243         this.dragable = true;
35244         this.pinching = false;
35245         
35246         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35247             this.dragable = false;
35248             return;
35249         }
35250         
35251         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35252         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35253         
35254     },
35255     
35256     onMouseMove : function(e)
35257     {   
35258         e.stopEvent();
35259         
35260         if(!this.canvasLoaded){
35261             return;
35262         }
35263         
35264         if (!this.dragable){
35265             return;
35266         }
35267         
35268         var minX = Math.ceil(this.thumbEl.getLeft(true));
35269         var minY = Math.ceil(this.thumbEl.getTop(true));
35270         
35271         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35272         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35273         
35274         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35275         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35276         
35277         x = x - this.mouseX;
35278         y = y - this.mouseY;
35279         
35280         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35281         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35282         
35283         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35284         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35285         
35286         this.previewEl.setLeft(bgX);
35287         this.previewEl.setTop(bgY);
35288         
35289         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35290         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35291     },
35292     
35293     onMouseUp : function(e)
35294     {   
35295         e.stopEvent();
35296         
35297         this.dragable = false;
35298     },
35299     
35300     onMouseWheel : function(e)
35301     {   
35302         e.stopEvent();
35303         
35304         this.startScale = this.scale;
35305         
35306         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35307         
35308         if(!this.zoomable()){
35309             this.scale = this.startScale;
35310             return;
35311         }
35312         
35313         this.draw();
35314         
35315         return;
35316     },
35317     
35318     zoomable : function()
35319     {
35320         var minScale = this.thumbEl.getWidth() / this.minWidth;
35321         
35322         if(this.minWidth < this.minHeight){
35323             minScale = this.thumbEl.getHeight() / this.minHeight;
35324         }
35325         
35326         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35327         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35328         
35329         if(
35330                 this.isDocument &&
35331                 (this.rotate == 0 || this.rotate == 180) && 
35332                 (
35333                     width > this.imageEl.OriginWidth || 
35334                     height > this.imageEl.OriginHeight ||
35335                     (width < this.minWidth && height < this.minHeight)
35336                 )
35337         ){
35338             return false;
35339         }
35340         
35341         if(
35342                 this.isDocument &&
35343                 (this.rotate == 90 || this.rotate == 270) && 
35344                 (
35345                     width > this.imageEl.OriginWidth || 
35346                     height > this.imageEl.OriginHeight ||
35347                     (width < this.minHeight && height < this.minWidth)
35348                 )
35349         ){
35350             return false;
35351         }
35352         
35353         if(
35354                 !this.isDocument &&
35355                 (this.rotate == 0 || this.rotate == 180) && 
35356                 (
35357                     width < this.minWidth || 
35358                     width > this.imageEl.OriginWidth || 
35359                     height < this.minHeight || 
35360                     height > this.imageEl.OriginHeight
35361                 )
35362         ){
35363             return false;
35364         }
35365         
35366         if(
35367                 !this.isDocument &&
35368                 (this.rotate == 90 || this.rotate == 270) && 
35369                 (
35370                     width < this.minHeight || 
35371                     width > this.imageEl.OriginWidth || 
35372                     height < this.minWidth || 
35373                     height > this.imageEl.OriginHeight
35374                 )
35375         ){
35376             return false;
35377         }
35378         
35379         return true;
35380         
35381     },
35382     
35383     onRotateLeft : function(e)
35384     {   
35385         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35386             
35387             var minScale = this.thumbEl.getWidth() / this.minWidth;
35388             
35389             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35390             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35391             
35392             this.startScale = this.scale;
35393             
35394             while (this.getScaleLevel() < minScale){
35395             
35396                 this.scale = this.scale + 1;
35397                 
35398                 if(!this.zoomable()){
35399                     break;
35400                 }
35401                 
35402                 if(
35403                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35404                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35405                 ){
35406                     continue;
35407                 }
35408                 
35409                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35410
35411                 this.draw();
35412                 
35413                 return;
35414             }
35415             
35416             this.scale = this.startScale;
35417             
35418             this.onRotateFail();
35419             
35420             return false;
35421         }
35422         
35423         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35424
35425         if(this.isDocument){
35426             this.setThumbBoxSize();
35427             this.setThumbBoxPosition();
35428             this.setCanvasPosition();
35429         }
35430         
35431         this.draw();
35432         
35433         this.fireEvent('rotate', this, 'left');
35434         
35435     },
35436     
35437     onRotateRight : function(e)
35438     {
35439         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35440             
35441             var minScale = this.thumbEl.getWidth() / this.minWidth;
35442         
35443             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35444             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35445             
35446             this.startScale = this.scale;
35447             
35448             while (this.getScaleLevel() < minScale){
35449             
35450                 this.scale = this.scale + 1;
35451                 
35452                 if(!this.zoomable()){
35453                     break;
35454                 }
35455                 
35456                 if(
35457                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35458                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35459                 ){
35460                     continue;
35461                 }
35462                 
35463                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35464
35465                 this.draw();
35466                 
35467                 return;
35468             }
35469             
35470             this.scale = this.startScale;
35471             
35472             this.onRotateFail();
35473             
35474             return false;
35475         }
35476         
35477         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35478
35479         if(this.isDocument){
35480             this.setThumbBoxSize();
35481             this.setThumbBoxPosition();
35482             this.setCanvasPosition();
35483         }
35484         
35485         this.draw();
35486         
35487         this.fireEvent('rotate', this, 'right');
35488     },
35489     
35490     onRotateFail : function()
35491     {
35492         this.errorEl.show(true);
35493         
35494         var _this = this;
35495         
35496         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35497     },
35498     
35499     draw : function()
35500     {
35501         this.previewEl.dom.innerHTML = '';
35502         
35503         var canvasEl = document.createElement("canvas");
35504         
35505         var contextEl = canvasEl.getContext("2d");
35506         
35507         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35508         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35509         var center = this.imageEl.OriginWidth / 2;
35510         
35511         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35512             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35513             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35514             center = this.imageEl.OriginHeight / 2;
35515         }
35516         
35517         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35518         
35519         contextEl.translate(center, center);
35520         contextEl.rotate(this.rotate * Math.PI / 180);
35521
35522         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35523         
35524         this.canvasEl = document.createElement("canvas");
35525         
35526         this.contextEl = this.canvasEl.getContext("2d");
35527         
35528         switch (this.rotate) {
35529             case 0 :
35530                 
35531                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35532                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35533                 
35534                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35535                 
35536                 break;
35537             case 90 : 
35538                 
35539                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35540                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35541                 
35542                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35543                     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);
35544                     break;
35545                 }
35546                 
35547                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35548                 
35549                 break;
35550             case 180 :
35551                 
35552                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35553                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35554                 
35555                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35556                     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);
35557                     break;
35558                 }
35559                 
35560                 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);
35561                 
35562                 break;
35563             case 270 :
35564                 
35565                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35566                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35567         
35568                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35569                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35570                     break;
35571                 }
35572                 
35573                 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);
35574                 
35575                 break;
35576             default : 
35577                 break;
35578         }
35579         
35580         this.previewEl.appendChild(this.canvasEl);
35581         
35582         this.setCanvasPosition();
35583     },
35584     
35585     crop : function()
35586     {
35587         if(!this.canvasLoaded){
35588             return;
35589         }
35590         
35591         var imageCanvas = document.createElement("canvas");
35592         
35593         var imageContext = imageCanvas.getContext("2d");
35594         
35595         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35596         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35597         
35598         var center = imageCanvas.width / 2;
35599         
35600         imageContext.translate(center, center);
35601         
35602         imageContext.rotate(this.rotate * Math.PI / 180);
35603         
35604         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35605         
35606         var canvas = document.createElement("canvas");
35607         
35608         var context = canvas.getContext("2d");
35609                 
35610         canvas.width = this.minWidth;
35611         canvas.height = this.minHeight;
35612
35613         switch (this.rotate) {
35614             case 0 :
35615                 
35616                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35617                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35618                 
35619                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35620                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35621                 
35622                 var targetWidth = this.minWidth - 2 * x;
35623                 var targetHeight = this.minHeight - 2 * y;
35624                 
35625                 var scale = 1;
35626                 
35627                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35628                     scale = targetWidth / width;
35629                 }
35630                 
35631                 if(x > 0 && y == 0){
35632                     scale = targetHeight / height;
35633                 }
35634                 
35635                 if(x > 0 && y > 0){
35636                     scale = targetWidth / width;
35637                     
35638                     if(width < height){
35639                         scale = targetHeight / height;
35640                     }
35641                 }
35642                 
35643                 context.scale(scale, scale);
35644                 
35645                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35646                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35647
35648                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35649                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35650
35651                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35652                 
35653                 break;
35654             case 90 : 
35655                 
35656                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35657                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35658                 
35659                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35660                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35661                 
35662                 var targetWidth = this.minWidth - 2 * x;
35663                 var targetHeight = this.minHeight - 2 * y;
35664                 
35665                 var scale = 1;
35666                 
35667                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35668                     scale = targetWidth / width;
35669                 }
35670                 
35671                 if(x > 0 && y == 0){
35672                     scale = targetHeight / height;
35673                 }
35674                 
35675                 if(x > 0 && y > 0){
35676                     scale = targetWidth / width;
35677                     
35678                     if(width < height){
35679                         scale = targetHeight / height;
35680                     }
35681                 }
35682                 
35683                 context.scale(scale, scale);
35684                 
35685                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35686                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35687
35688                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35689                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35690                 
35691                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35692                 
35693                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35694                 
35695                 break;
35696             case 180 :
35697                 
35698                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35699                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35700                 
35701                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35702                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35703                 
35704                 var targetWidth = this.minWidth - 2 * x;
35705                 var targetHeight = this.minHeight - 2 * y;
35706                 
35707                 var scale = 1;
35708                 
35709                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35710                     scale = targetWidth / width;
35711                 }
35712                 
35713                 if(x > 0 && y == 0){
35714                     scale = targetHeight / height;
35715                 }
35716                 
35717                 if(x > 0 && y > 0){
35718                     scale = targetWidth / width;
35719                     
35720                     if(width < height){
35721                         scale = targetHeight / height;
35722                     }
35723                 }
35724                 
35725                 context.scale(scale, scale);
35726                 
35727                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35728                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35729
35730                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35731                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35732
35733                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35734                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35735                 
35736                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35737                 
35738                 break;
35739             case 270 :
35740                 
35741                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35742                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35743                 
35744                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35745                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35746                 
35747                 var targetWidth = this.minWidth - 2 * x;
35748                 var targetHeight = this.minHeight - 2 * y;
35749                 
35750                 var scale = 1;
35751                 
35752                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35753                     scale = targetWidth / width;
35754                 }
35755                 
35756                 if(x > 0 && y == 0){
35757                     scale = targetHeight / height;
35758                 }
35759                 
35760                 if(x > 0 && y > 0){
35761                     scale = targetWidth / width;
35762                     
35763                     if(width < height){
35764                         scale = targetHeight / height;
35765                     }
35766                 }
35767                 
35768                 context.scale(scale, scale);
35769                 
35770                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35771                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35772
35773                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35774                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35775                 
35776                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35777                 
35778                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35779                 
35780                 break;
35781             default : 
35782                 break;
35783         }
35784         
35785         this.cropData = canvas.toDataURL(this.cropType);
35786         
35787         if(this.fireEvent('crop', this, this.cropData) !== false){
35788             this.process(this.file, this.cropData);
35789         }
35790         
35791         return;
35792         
35793     },
35794     
35795     setThumbBoxSize : function()
35796     {
35797         var width, height;
35798         
35799         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
35800             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
35801             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
35802             
35803             this.minWidth = width;
35804             this.minHeight = height;
35805             
35806             if(this.rotate == 90 || this.rotate == 270){
35807                 this.minWidth = height;
35808                 this.minHeight = width;
35809             }
35810         }
35811         
35812         height = 300;
35813         width = Math.ceil(this.minWidth * height / this.minHeight);
35814         
35815         if(this.minWidth > this.minHeight){
35816             width = 300;
35817             height = Math.ceil(this.minHeight * width / this.minWidth);
35818         }
35819         
35820         this.thumbEl.setStyle({
35821             width : width + 'px',
35822             height : height + 'px'
35823         });
35824
35825         return;
35826             
35827     },
35828     
35829     setThumbBoxPosition : function()
35830     {
35831         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
35832         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
35833         
35834         this.thumbEl.setLeft(x);
35835         this.thumbEl.setTop(y);
35836         
35837     },
35838     
35839     baseRotateLevel : function()
35840     {
35841         this.baseRotate = 1;
35842         
35843         if(
35844                 typeof(this.exif) != 'undefined' &&
35845                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
35846                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
35847         ){
35848             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
35849         }
35850         
35851         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
35852         
35853     },
35854     
35855     baseScaleLevel : function()
35856     {
35857         var width, height;
35858         
35859         if(this.isDocument){
35860             
35861             if(this.baseRotate == 6 || this.baseRotate == 8){
35862             
35863                 height = this.thumbEl.getHeight();
35864                 this.baseScale = height / this.imageEl.OriginWidth;
35865
35866                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
35867                     width = this.thumbEl.getWidth();
35868                     this.baseScale = width / this.imageEl.OriginHeight;
35869                 }
35870
35871                 return;
35872             }
35873
35874             height = this.thumbEl.getHeight();
35875             this.baseScale = height / this.imageEl.OriginHeight;
35876
35877             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
35878                 width = this.thumbEl.getWidth();
35879                 this.baseScale = width / this.imageEl.OriginWidth;
35880             }
35881
35882             return;
35883         }
35884         
35885         if(this.baseRotate == 6 || this.baseRotate == 8){
35886             
35887             width = this.thumbEl.getHeight();
35888             this.baseScale = width / this.imageEl.OriginHeight;
35889             
35890             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
35891                 height = this.thumbEl.getWidth();
35892                 this.baseScale = height / this.imageEl.OriginHeight;
35893             }
35894             
35895             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35896                 height = this.thumbEl.getWidth();
35897                 this.baseScale = height / this.imageEl.OriginHeight;
35898                 
35899                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
35900                     width = this.thumbEl.getHeight();
35901                     this.baseScale = width / this.imageEl.OriginWidth;
35902                 }
35903             }
35904             
35905             return;
35906         }
35907         
35908         width = this.thumbEl.getWidth();
35909         this.baseScale = width / this.imageEl.OriginWidth;
35910         
35911         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
35912             height = this.thumbEl.getHeight();
35913             this.baseScale = height / this.imageEl.OriginHeight;
35914         }
35915         
35916         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35917             
35918             height = this.thumbEl.getHeight();
35919             this.baseScale = height / this.imageEl.OriginHeight;
35920             
35921             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
35922                 width = this.thumbEl.getWidth();
35923                 this.baseScale = width / this.imageEl.OriginWidth;
35924             }
35925             
35926         }
35927         
35928         return;
35929     },
35930     
35931     getScaleLevel : function()
35932     {
35933         return this.baseScale * Math.pow(1.1, this.scale);
35934     },
35935     
35936     onTouchStart : function(e)
35937     {
35938         if(!this.canvasLoaded){
35939             this.beforeSelectFile(e);
35940             return;
35941         }
35942         
35943         var touches = e.browserEvent.touches;
35944         
35945         if(!touches){
35946             return;
35947         }
35948         
35949         if(touches.length == 1){
35950             this.onMouseDown(e);
35951             return;
35952         }
35953         
35954         if(touches.length != 2){
35955             return;
35956         }
35957         
35958         var coords = [];
35959         
35960         for(var i = 0, finger; finger = touches[i]; i++){
35961             coords.push(finger.pageX, finger.pageY);
35962         }
35963         
35964         var x = Math.pow(coords[0] - coords[2], 2);
35965         var y = Math.pow(coords[1] - coords[3], 2);
35966         
35967         this.startDistance = Math.sqrt(x + y);
35968         
35969         this.startScale = this.scale;
35970         
35971         this.pinching = true;
35972         this.dragable = false;
35973         
35974     },
35975     
35976     onTouchMove : function(e)
35977     {
35978         if(!this.pinching && !this.dragable){
35979             return;
35980         }
35981         
35982         var touches = e.browserEvent.touches;
35983         
35984         if(!touches){
35985             return;
35986         }
35987         
35988         if(this.dragable){
35989             this.onMouseMove(e);
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.endDistance = Math.sqrt(x + y);
36003         
36004         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36005         
36006         if(!this.zoomable()){
36007             this.scale = this.startScale;
36008             return;
36009         }
36010         
36011         this.draw();
36012         
36013     },
36014     
36015     onTouchEnd : function(e)
36016     {
36017         this.pinching = false;
36018         this.dragable = false;
36019         
36020     },
36021     
36022     process : function(file, crop)
36023     {
36024         if(this.loadMask){
36025             this.maskEl.mask(this.loadingText);
36026         }
36027         
36028         this.xhr = new XMLHttpRequest();
36029         
36030         file.xhr = this.xhr;
36031
36032         this.xhr.open(this.method, this.url, true);
36033         
36034         var headers = {
36035             "Accept": "application/json",
36036             "Cache-Control": "no-cache",
36037             "X-Requested-With": "XMLHttpRequest"
36038         };
36039         
36040         for (var headerName in headers) {
36041             var headerValue = headers[headerName];
36042             if (headerValue) {
36043                 this.xhr.setRequestHeader(headerName, headerValue);
36044             }
36045         }
36046         
36047         var _this = this;
36048         
36049         this.xhr.onload = function()
36050         {
36051             _this.xhrOnLoad(_this.xhr);
36052         }
36053         
36054         this.xhr.onerror = function()
36055         {
36056             _this.xhrOnError(_this.xhr);
36057         }
36058         
36059         var formData = new FormData();
36060
36061         formData.append('returnHTML', 'NO');
36062         
36063         if(crop){
36064             formData.append('crop', crop);
36065         }
36066         
36067         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36068             formData.append(this.paramName, file, file.name);
36069         }
36070         
36071         if(typeof(file.filename) != 'undefined'){
36072             formData.append('filename', file.filename);
36073         }
36074         
36075         if(typeof(file.mimetype) != 'undefined'){
36076             formData.append('mimetype', file.mimetype);
36077         }
36078         
36079         if(this.fireEvent('arrange', this, formData) != false){
36080             this.xhr.send(formData);
36081         };
36082     },
36083     
36084     xhrOnLoad : function(xhr)
36085     {
36086         if(this.loadMask){
36087             this.maskEl.unmask();
36088         }
36089         
36090         if (xhr.readyState !== 4) {
36091             this.fireEvent('exception', this, xhr);
36092             return;
36093         }
36094
36095         var response = Roo.decode(xhr.responseText);
36096         
36097         if(!response.success){
36098             this.fireEvent('exception', this, xhr);
36099             return;
36100         }
36101         
36102         var response = Roo.decode(xhr.responseText);
36103         
36104         this.fireEvent('upload', this, response);
36105         
36106     },
36107     
36108     xhrOnError : function()
36109     {
36110         if(this.loadMask){
36111             this.maskEl.unmask();
36112         }
36113         
36114         Roo.log('xhr on error');
36115         
36116         var response = Roo.decode(xhr.responseText);
36117           
36118         Roo.log(response);
36119         
36120     },
36121     
36122     prepare : function(file)
36123     {   
36124         if(this.loadMask){
36125             this.maskEl.mask(this.loadingText);
36126         }
36127         
36128         this.file = false;
36129         this.exif = {};
36130         
36131         if(typeof(file) === 'string'){
36132             this.loadCanvas(file);
36133             return;
36134         }
36135         
36136         if(!file || !this.urlAPI){
36137             return;
36138         }
36139         
36140         this.file = file;
36141         this.cropType = file.type;
36142         
36143         var _this = this;
36144         
36145         if(this.fireEvent('prepare', this, this.file) != false){
36146             
36147             var reader = new FileReader();
36148             
36149             reader.onload = function (e) {
36150                 if (e.target.error) {
36151                     Roo.log(e.target.error);
36152                     return;
36153                 }
36154                 
36155                 var buffer = e.target.result,
36156                     dataView = new DataView(buffer),
36157                     offset = 2,
36158                     maxOffset = dataView.byteLength - 4,
36159                     markerBytes,
36160                     markerLength;
36161                 
36162                 if (dataView.getUint16(0) === 0xffd8) {
36163                     while (offset < maxOffset) {
36164                         markerBytes = dataView.getUint16(offset);
36165                         
36166                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36167                             markerLength = dataView.getUint16(offset + 2) + 2;
36168                             if (offset + markerLength > dataView.byteLength) {
36169                                 Roo.log('Invalid meta data: Invalid segment size.');
36170                                 break;
36171                             }
36172                             
36173                             if(markerBytes == 0xffe1){
36174                                 _this.parseExifData(
36175                                     dataView,
36176                                     offset,
36177                                     markerLength
36178                                 );
36179                             }
36180                             
36181                             offset += markerLength;
36182                             
36183                             continue;
36184                         }
36185                         
36186                         break;
36187                     }
36188                     
36189                 }
36190                 
36191                 var url = _this.urlAPI.createObjectURL(_this.file);
36192                 
36193                 _this.loadCanvas(url);
36194                 
36195                 return;
36196             }
36197             
36198             reader.readAsArrayBuffer(this.file);
36199             
36200         }
36201         
36202     },
36203     
36204     parseExifData : function(dataView, offset, length)
36205     {
36206         var tiffOffset = offset + 10,
36207             littleEndian,
36208             dirOffset;
36209     
36210         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36211             // No Exif data, might be XMP data instead
36212             return;
36213         }
36214         
36215         // Check for the ASCII code for "Exif" (0x45786966):
36216         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36217             // No Exif data, might be XMP data instead
36218             return;
36219         }
36220         if (tiffOffset + 8 > dataView.byteLength) {
36221             Roo.log('Invalid Exif data: Invalid segment size.');
36222             return;
36223         }
36224         // Check for the two null bytes:
36225         if (dataView.getUint16(offset + 8) !== 0x0000) {
36226             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36227             return;
36228         }
36229         // Check the byte alignment:
36230         switch (dataView.getUint16(tiffOffset)) {
36231         case 0x4949:
36232             littleEndian = true;
36233             break;
36234         case 0x4D4D:
36235             littleEndian = false;
36236             break;
36237         default:
36238             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36239             return;
36240         }
36241         // Check for the TIFF tag marker (0x002A):
36242         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36243             Roo.log('Invalid Exif data: Missing TIFF marker.');
36244             return;
36245         }
36246         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36247         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36248         
36249         this.parseExifTags(
36250             dataView,
36251             tiffOffset,
36252             tiffOffset + dirOffset,
36253             littleEndian
36254         );
36255     },
36256     
36257     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36258     {
36259         var tagsNumber,
36260             dirEndOffset,
36261             i;
36262         if (dirOffset + 6 > dataView.byteLength) {
36263             Roo.log('Invalid Exif data: Invalid directory offset.');
36264             return;
36265         }
36266         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36267         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36268         if (dirEndOffset + 4 > dataView.byteLength) {
36269             Roo.log('Invalid Exif data: Invalid directory size.');
36270             return;
36271         }
36272         for (i = 0; i < tagsNumber; i += 1) {
36273             this.parseExifTag(
36274                 dataView,
36275                 tiffOffset,
36276                 dirOffset + 2 + 12 * i, // tag offset
36277                 littleEndian
36278             );
36279         }
36280         // Return the offset to the next directory:
36281         return dataView.getUint32(dirEndOffset, littleEndian);
36282     },
36283     
36284     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36285     {
36286         var tag = dataView.getUint16(offset, littleEndian);
36287         
36288         this.exif[tag] = this.getExifValue(
36289             dataView,
36290             tiffOffset,
36291             offset,
36292             dataView.getUint16(offset + 2, littleEndian), // tag type
36293             dataView.getUint32(offset + 4, littleEndian), // tag length
36294             littleEndian
36295         );
36296     },
36297     
36298     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36299     {
36300         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36301             tagSize,
36302             dataOffset,
36303             values,
36304             i,
36305             str,
36306             c;
36307     
36308         if (!tagType) {
36309             Roo.log('Invalid Exif data: Invalid tag type.');
36310             return;
36311         }
36312         
36313         tagSize = tagType.size * length;
36314         // Determine if the value is contained in the dataOffset bytes,
36315         // or if the value at the dataOffset is a pointer to the actual data:
36316         dataOffset = tagSize > 4 ?
36317                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36318         if (dataOffset + tagSize > dataView.byteLength) {
36319             Roo.log('Invalid Exif data: Invalid data offset.');
36320             return;
36321         }
36322         if (length === 1) {
36323             return tagType.getValue(dataView, dataOffset, littleEndian);
36324         }
36325         values = [];
36326         for (i = 0; i < length; i += 1) {
36327             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36328         }
36329         
36330         if (tagType.ascii) {
36331             str = '';
36332             // Concatenate the chars:
36333             for (i = 0; i < values.length; i += 1) {
36334                 c = values[i];
36335                 // Ignore the terminating NULL byte(s):
36336                 if (c === '\u0000') {
36337                     break;
36338                 }
36339                 str += c;
36340             }
36341             return str;
36342         }
36343         return values;
36344     }
36345     
36346 });
36347
36348 Roo.apply(Roo.bootstrap.UploadCropbox, {
36349     tags : {
36350         'Orientation': 0x0112
36351     },
36352     
36353     Orientation: {
36354             1: 0, //'top-left',
36355 //            2: 'top-right',
36356             3: 180, //'bottom-right',
36357 //            4: 'bottom-left',
36358 //            5: 'left-top',
36359             6: 90, //'right-top',
36360 //            7: 'right-bottom',
36361             8: 270 //'left-bottom'
36362     },
36363     
36364     exifTagTypes : {
36365         // byte, 8-bit unsigned int:
36366         1: {
36367             getValue: function (dataView, dataOffset) {
36368                 return dataView.getUint8(dataOffset);
36369             },
36370             size: 1
36371         },
36372         // ascii, 8-bit byte:
36373         2: {
36374             getValue: function (dataView, dataOffset) {
36375                 return String.fromCharCode(dataView.getUint8(dataOffset));
36376             },
36377             size: 1,
36378             ascii: true
36379         },
36380         // short, 16 bit int:
36381         3: {
36382             getValue: function (dataView, dataOffset, littleEndian) {
36383                 return dataView.getUint16(dataOffset, littleEndian);
36384             },
36385             size: 2
36386         },
36387         // long, 32 bit int:
36388         4: {
36389             getValue: function (dataView, dataOffset, littleEndian) {
36390                 return dataView.getUint32(dataOffset, littleEndian);
36391             },
36392             size: 4
36393         },
36394         // rational = two long values, first is numerator, second is denominator:
36395         5: {
36396             getValue: function (dataView, dataOffset, littleEndian) {
36397                 return dataView.getUint32(dataOffset, littleEndian) /
36398                     dataView.getUint32(dataOffset + 4, littleEndian);
36399             },
36400             size: 8
36401         },
36402         // slong, 32 bit signed int:
36403         9: {
36404             getValue: function (dataView, dataOffset, littleEndian) {
36405                 return dataView.getInt32(dataOffset, littleEndian);
36406             },
36407             size: 4
36408         },
36409         // srational, two slongs, first is numerator, second is denominator:
36410         10: {
36411             getValue: function (dataView, dataOffset, littleEndian) {
36412                 return dataView.getInt32(dataOffset, littleEndian) /
36413                     dataView.getInt32(dataOffset + 4, littleEndian);
36414             },
36415             size: 8
36416         }
36417     },
36418     
36419     footer : {
36420         STANDARD : [
36421             {
36422                 tag : 'div',
36423                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36424                 action : 'rotate-left',
36425                 cn : [
36426                     {
36427                         tag : 'button',
36428                         cls : 'btn btn-default',
36429                         html : '<i class="fa fa-undo"></i>'
36430                     }
36431                 ]
36432             },
36433             {
36434                 tag : 'div',
36435                 cls : 'btn-group roo-upload-cropbox-picture',
36436                 action : 'picture',
36437                 cn : [
36438                     {
36439                         tag : 'button',
36440                         cls : 'btn btn-default',
36441                         html : '<i class="fa fa-picture-o"></i>'
36442                     }
36443                 ]
36444             },
36445             {
36446                 tag : 'div',
36447                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36448                 action : 'rotate-right',
36449                 cn : [
36450                     {
36451                         tag : 'button',
36452                         cls : 'btn btn-default',
36453                         html : '<i class="fa fa-repeat"></i>'
36454                     }
36455                 ]
36456             }
36457         ],
36458         DOCUMENT : [
36459             {
36460                 tag : 'div',
36461                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36462                 action : 'rotate-left',
36463                 cn : [
36464                     {
36465                         tag : 'button',
36466                         cls : 'btn btn-default',
36467                         html : '<i class="fa fa-undo"></i>'
36468                     }
36469                 ]
36470             },
36471             {
36472                 tag : 'div',
36473                 cls : 'btn-group roo-upload-cropbox-download',
36474                 action : 'download',
36475                 cn : [
36476                     {
36477                         tag : 'button',
36478                         cls : 'btn btn-default',
36479                         html : '<i class="fa fa-download"></i>'
36480                     }
36481                 ]
36482             },
36483             {
36484                 tag : 'div',
36485                 cls : 'btn-group roo-upload-cropbox-crop',
36486                 action : 'crop',
36487                 cn : [
36488                     {
36489                         tag : 'button',
36490                         cls : 'btn btn-default',
36491                         html : '<i class="fa fa-crop"></i>'
36492                     }
36493                 ]
36494             },
36495             {
36496                 tag : 'div',
36497                 cls : 'btn-group roo-upload-cropbox-trash',
36498                 action : 'trash',
36499                 cn : [
36500                     {
36501                         tag : 'button',
36502                         cls : 'btn btn-default',
36503                         html : '<i class="fa fa-trash"></i>'
36504                     }
36505                 ]
36506             },
36507             {
36508                 tag : 'div',
36509                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36510                 action : 'rotate-right',
36511                 cn : [
36512                     {
36513                         tag : 'button',
36514                         cls : 'btn btn-default',
36515                         html : '<i class="fa fa-repeat"></i>'
36516                     }
36517                 ]
36518             }
36519         ],
36520         ROTATOR : [
36521             {
36522                 tag : 'div',
36523                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36524                 action : 'rotate-left',
36525                 cn : [
36526                     {
36527                         tag : 'button',
36528                         cls : 'btn btn-default',
36529                         html : '<i class="fa fa-undo"></i>'
36530                     }
36531                 ]
36532             },
36533             {
36534                 tag : 'div',
36535                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36536                 action : 'rotate-right',
36537                 cn : [
36538                     {
36539                         tag : 'button',
36540                         cls : 'btn btn-default',
36541                         html : '<i class="fa fa-repeat"></i>'
36542                     }
36543                 ]
36544             }
36545         ]
36546     }
36547 });
36548
36549 /*
36550 * Licence: LGPL
36551 */
36552
36553 /**
36554  * @class Roo.bootstrap.DocumentManager
36555  * @extends Roo.bootstrap.Component
36556  * Bootstrap DocumentManager class
36557  * @cfg {String} paramName default 'imageUpload'
36558  * @cfg {String} toolTipName default 'filename'
36559  * @cfg {String} method default POST
36560  * @cfg {String} url action url
36561  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
36562  * @cfg {Boolean} multiple multiple upload default true
36563  * @cfg {Number} thumbSize default 300
36564  * @cfg {String} fieldLabel
36565  * @cfg {Number} labelWidth default 4
36566  * @cfg {String} labelAlign (left|top) default left
36567  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
36568 * @cfg {Number} labellg set the width of label (1-12)
36569  * @cfg {Number} labelmd set the width of label (1-12)
36570  * @cfg {Number} labelsm set the width of label (1-12)
36571  * @cfg {Number} labelxs set the width of label (1-12)
36572  * 
36573  * @constructor
36574  * Create a new DocumentManager
36575  * @param {Object} config The config object
36576  */
36577
36578 Roo.bootstrap.DocumentManager = function(config){
36579     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
36580     
36581     this.files = [];
36582     this.delegates = [];
36583     
36584     this.addEvents({
36585         /**
36586          * @event initial
36587          * Fire when initial the DocumentManager
36588          * @param {Roo.bootstrap.DocumentManager} this
36589          */
36590         "initial" : true,
36591         /**
36592          * @event inspect
36593          * inspect selected file
36594          * @param {Roo.bootstrap.DocumentManager} this
36595          * @param {File} file
36596          */
36597         "inspect" : true,
36598         /**
36599          * @event exception
36600          * Fire when xhr load exception
36601          * @param {Roo.bootstrap.DocumentManager} this
36602          * @param {XMLHttpRequest} xhr
36603          */
36604         "exception" : true,
36605         /**
36606          * @event afterupload
36607          * Fire when xhr load exception
36608          * @param {Roo.bootstrap.DocumentManager} this
36609          * @param {XMLHttpRequest} xhr
36610          */
36611         "afterupload" : true,
36612         /**
36613          * @event prepare
36614          * prepare the form data
36615          * @param {Roo.bootstrap.DocumentManager} this
36616          * @param {Object} formData
36617          */
36618         "prepare" : true,
36619         /**
36620          * @event remove
36621          * Fire when remove the file
36622          * @param {Roo.bootstrap.DocumentManager} this
36623          * @param {Object} file
36624          */
36625         "remove" : true,
36626         /**
36627          * @event refresh
36628          * Fire after refresh the file
36629          * @param {Roo.bootstrap.DocumentManager} this
36630          */
36631         "refresh" : true,
36632         /**
36633          * @event click
36634          * Fire after click the image
36635          * @param {Roo.bootstrap.DocumentManager} this
36636          * @param {Object} file
36637          */
36638         "click" : true,
36639         /**
36640          * @event edit
36641          * Fire when upload a image and editable set to true
36642          * @param {Roo.bootstrap.DocumentManager} this
36643          * @param {Object} file
36644          */
36645         "edit" : true,
36646         /**
36647          * @event beforeselectfile
36648          * Fire before select file
36649          * @param {Roo.bootstrap.DocumentManager} this
36650          */
36651         "beforeselectfile" : true,
36652         /**
36653          * @event process
36654          * Fire before process file
36655          * @param {Roo.bootstrap.DocumentManager} this
36656          * @param {Object} file
36657          */
36658         "process" : true,
36659         /**
36660          * @event previewrendered
36661          * Fire when preview rendered
36662          * @param {Roo.bootstrap.DocumentManager} this
36663          * @param {Object} file
36664          */
36665         "previewrendered" : true,
36666         /**
36667          */
36668         "previewResize" : true
36669         
36670     });
36671 };
36672
36673 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
36674     
36675     boxes : 0,
36676     inputName : '',
36677     thumbSize : 300,
36678     multiple : true,
36679     files : false,
36680     method : 'POST',
36681     url : '',
36682     paramName : 'imageUpload',
36683     toolTipName : 'filename',
36684     fieldLabel : '',
36685     labelWidth : 4,
36686     labelAlign : 'left',
36687     editable : true,
36688     delegates : false,
36689     xhr : false, 
36690     
36691     labellg : 0,
36692     labelmd : 0,
36693     labelsm : 0,
36694     labelxs : 0,
36695     
36696     getAutoCreate : function()
36697     {   
36698         var managerWidget = {
36699             tag : 'div',
36700             cls : 'roo-document-manager',
36701             cn : [
36702                 {
36703                     tag : 'input',
36704                     cls : 'roo-document-manager-selector',
36705                     type : 'file'
36706                 },
36707                 {
36708                     tag : 'div',
36709                     cls : 'roo-document-manager-uploader',
36710                     cn : [
36711                         {
36712                             tag : 'div',
36713                             cls : 'roo-document-manager-upload-btn',
36714                             html : '<i class="fa fa-plus"></i>'
36715                         }
36716                     ]
36717                     
36718                 }
36719             ]
36720         };
36721         
36722         var content = [
36723             {
36724                 tag : 'div',
36725                 cls : 'column col-md-12',
36726                 cn : managerWidget
36727             }
36728         ];
36729         
36730         if(this.fieldLabel.length){
36731             
36732             content = [
36733                 {
36734                     tag : 'div',
36735                     cls : 'column col-md-12',
36736                     html : this.fieldLabel
36737                 },
36738                 {
36739                     tag : 'div',
36740                     cls : 'column col-md-12',
36741                     cn : managerWidget
36742                 }
36743             ];
36744
36745             if(this.labelAlign == 'left'){
36746                 content = [
36747                     {
36748                         tag : 'div',
36749                         cls : 'column',
36750                         html : this.fieldLabel
36751                     },
36752                     {
36753                         tag : 'div',
36754                         cls : 'column',
36755                         cn : managerWidget
36756                     }
36757                 ];
36758                 
36759                 if(this.labelWidth > 12){
36760                     content[0].style = "width: " + this.labelWidth + 'px';
36761                 }
36762
36763                 if(this.labelWidth < 13 && this.labelmd == 0){
36764                     this.labelmd = this.labelWidth;
36765                 }
36766
36767                 if(this.labellg > 0){
36768                     content[0].cls += ' col-lg-' + this.labellg;
36769                     content[1].cls += ' col-lg-' + (12 - this.labellg);
36770                 }
36771
36772                 if(this.labelmd > 0){
36773                     content[0].cls += ' col-md-' + this.labelmd;
36774                     content[1].cls += ' col-md-' + (12 - this.labelmd);
36775                 }
36776
36777                 if(this.labelsm > 0){
36778                     content[0].cls += ' col-sm-' + this.labelsm;
36779                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
36780                 }
36781
36782                 if(this.labelxs > 0){
36783                     content[0].cls += ' col-xs-' + this.labelxs;
36784                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
36785                 }
36786                 
36787             }
36788         }
36789         
36790         var cfg = {
36791             tag : 'div',
36792             cls : 'row clearfix',
36793             cn : content
36794         };
36795         
36796         return cfg;
36797         
36798     },
36799     
36800     initEvents : function()
36801     {
36802         this.managerEl = this.el.select('.roo-document-manager', true).first();
36803         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36804         
36805         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
36806         this.selectorEl.hide();
36807         
36808         if(this.multiple){
36809             this.selectorEl.attr('multiple', 'multiple');
36810         }
36811         
36812         this.selectorEl.on('change', this.onFileSelected, this);
36813         
36814         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
36815         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36816         
36817         this.uploader.on('click', this.onUploaderClick, this);
36818         
36819         this.renderProgressDialog();
36820         
36821         var _this = this;
36822         
36823         window.addEventListener("resize", function() { _this.refresh(); } );
36824         
36825         this.fireEvent('initial', this);
36826     },
36827     
36828     renderProgressDialog : function()
36829     {
36830         var _this = this;
36831         
36832         this.progressDialog = new Roo.bootstrap.Modal({
36833             cls : 'roo-document-manager-progress-dialog',
36834             allow_close : false,
36835             animate : false,
36836             title : '',
36837             buttons : [
36838                 {
36839                     name  :'cancel',
36840                     weight : 'danger',
36841                     html : 'Cancel'
36842                 }
36843             ], 
36844             listeners : { 
36845                 btnclick : function() {
36846                     _this.uploadCancel();
36847                     this.hide();
36848                 }
36849             }
36850         });
36851          
36852         this.progressDialog.render(Roo.get(document.body));
36853          
36854         this.progress = new Roo.bootstrap.Progress({
36855             cls : 'roo-document-manager-progress',
36856             active : true,
36857             striped : true
36858         });
36859         
36860         this.progress.render(this.progressDialog.getChildContainer());
36861         
36862         this.progressBar = new Roo.bootstrap.ProgressBar({
36863             cls : 'roo-document-manager-progress-bar',
36864             aria_valuenow : 0,
36865             aria_valuemin : 0,
36866             aria_valuemax : 12,
36867             panel : 'success'
36868         });
36869         
36870         this.progressBar.render(this.progress.getChildContainer());
36871     },
36872     
36873     onUploaderClick : function(e)
36874     {
36875         e.preventDefault();
36876      
36877         if(this.fireEvent('beforeselectfile', this) != false){
36878             this.selectorEl.dom.click();
36879         }
36880         
36881     },
36882     
36883     onFileSelected : function(e)
36884     {
36885         e.preventDefault();
36886         
36887         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36888             return;
36889         }
36890         
36891         Roo.each(this.selectorEl.dom.files, function(file){
36892             if(this.fireEvent('inspect', this, file) != false){
36893                 this.files.push(file);
36894             }
36895         }, this);
36896         
36897         this.queue();
36898         
36899     },
36900     
36901     queue : function()
36902     {
36903         this.selectorEl.dom.value = '';
36904         
36905         if(!this.files || !this.files.length){
36906             return;
36907         }
36908         
36909         if(this.boxes > 0 && this.files.length > this.boxes){
36910             this.files = this.files.slice(0, this.boxes);
36911         }
36912         
36913         this.uploader.show();
36914         
36915         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36916             this.uploader.hide();
36917         }
36918         
36919         var _this = this;
36920         
36921         var files = [];
36922         
36923         var docs = [];
36924         
36925         Roo.each(this.files, function(file){
36926             
36927             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36928                 var f = this.renderPreview(file);
36929                 files.push(f);
36930                 return;
36931             }
36932             
36933             if(file.type.indexOf('image') != -1){
36934                 this.delegates.push(
36935                     (function(){
36936                         _this.process(file);
36937                     }).createDelegate(this)
36938                 );
36939         
36940                 return;
36941             }
36942             
36943             docs.push(
36944                 (function(){
36945                     _this.process(file);
36946                 }).createDelegate(this)
36947             );
36948             
36949         }, this);
36950         
36951         this.files = files;
36952         
36953         this.delegates = this.delegates.concat(docs);
36954         
36955         if(!this.delegates.length){
36956             this.refresh();
36957             return;
36958         }
36959         
36960         this.progressBar.aria_valuemax = this.delegates.length;
36961         
36962         this.arrange();
36963         
36964         return;
36965     },
36966     
36967     arrange : function()
36968     {
36969         if(!this.delegates.length){
36970             this.progressDialog.hide();
36971             this.refresh();
36972             return;
36973         }
36974         
36975         var delegate = this.delegates.shift();
36976         
36977         this.progressDialog.show();
36978         
36979         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
36980         
36981         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
36982         
36983         delegate();
36984     },
36985     
36986     refresh : function()
36987     {
36988         this.uploader.show();
36989         
36990         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36991             this.uploader.hide();
36992         }
36993         
36994         Roo.isTouch ? this.closable(false) : this.closable(true);
36995         
36996         this.fireEvent('refresh', this);
36997     },
36998     
36999     onRemove : function(e, el, o)
37000     {
37001         e.preventDefault();
37002         
37003         this.fireEvent('remove', this, o);
37004         
37005     },
37006     
37007     remove : function(o)
37008     {
37009         var files = [];
37010         
37011         Roo.each(this.files, function(file){
37012             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37013                 files.push(file);
37014                 return;
37015             }
37016
37017             o.target.remove();
37018
37019         }, this);
37020         
37021         this.files = files;
37022         
37023         this.refresh();
37024     },
37025     
37026     clear : function()
37027     {
37028         Roo.each(this.files, function(file){
37029             if(!file.target){
37030                 return;
37031             }
37032             
37033             file.target.remove();
37034
37035         }, this);
37036         
37037         this.files = [];
37038         
37039         this.refresh();
37040     },
37041     
37042     onClick : function(e, el, o)
37043     {
37044         e.preventDefault();
37045         
37046         this.fireEvent('click', this, o);
37047         
37048     },
37049     
37050     closable : function(closable)
37051     {
37052         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37053             
37054             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37055             
37056             if(closable){
37057                 el.show();
37058                 return;
37059             }
37060             
37061             el.hide();
37062             
37063         }, this);
37064     },
37065     
37066     xhrOnLoad : function(xhr)
37067     {
37068         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37069             el.remove();
37070         }, this);
37071         
37072         if (xhr.readyState !== 4) {
37073             this.arrange();
37074             this.fireEvent('exception', this, xhr);
37075             return;
37076         }
37077
37078         var response = Roo.decode(xhr.responseText);
37079         
37080         if(!response.success){
37081             this.arrange();
37082             this.fireEvent('exception', this, xhr);
37083             return;
37084         }
37085         
37086         var file = this.renderPreview(response.data);
37087         
37088         this.files.push(file);
37089         
37090         this.arrange();
37091         
37092         this.fireEvent('afterupload', this, xhr);
37093         
37094     },
37095     
37096     xhrOnError : function(xhr)
37097     {
37098         Roo.log('xhr on error');
37099         
37100         var response = Roo.decode(xhr.responseText);
37101           
37102         Roo.log(response);
37103         
37104         this.arrange();
37105     },
37106     
37107     process : function(file)
37108     {
37109         if(this.fireEvent('process', this, file) !== false){
37110             if(this.editable && file.type.indexOf('image') != -1){
37111                 this.fireEvent('edit', this, file);
37112                 return;
37113             }
37114
37115             this.uploadStart(file, false);
37116
37117             return;
37118         }
37119         
37120     },
37121     
37122     uploadStart : function(file, crop)
37123     {
37124         this.xhr = new XMLHttpRequest();
37125         
37126         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37127             this.arrange();
37128             return;
37129         }
37130         
37131         file.xhr = this.xhr;
37132             
37133         this.managerEl.createChild({
37134             tag : 'div',
37135             cls : 'roo-document-manager-loading',
37136             cn : [
37137                 {
37138                     tag : 'div',
37139                     tooltip : file.name,
37140                     cls : 'roo-document-manager-thumb',
37141                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37142                 }
37143             ]
37144
37145         });
37146
37147         this.xhr.open(this.method, this.url, true);
37148         
37149         var headers = {
37150             "Accept": "application/json",
37151             "Cache-Control": "no-cache",
37152             "X-Requested-With": "XMLHttpRequest"
37153         };
37154         
37155         for (var headerName in headers) {
37156             var headerValue = headers[headerName];
37157             if (headerValue) {
37158                 this.xhr.setRequestHeader(headerName, headerValue);
37159             }
37160         }
37161         
37162         var _this = this;
37163         
37164         this.xhr.onload = function()
37165         {
37166             _this.xhrOnLoad(_this.xhr);
37167         }
37168         
37169         this.xhr.onerror = function()
37170         {
37171             _this.xhrOnError(_this.xhr);
37172         }
37173         
37174         var formData = new FormData();
37175
37176         formData.append('returnHTML', 'NO');
37177         
37178         if(crop){
37179             formData.append('crop', crop);
37180         }
37181         
37182         formData.append(this.paramName, file, file.name);
37183         
37184         var options = {
37185             file : file, 
37186             manually : false
37187         };
37188         
37189         if(this.fireEvent('prepare', this, formData, options) != false){
37190             
37191             if(options.manually){
37192                 return;
37193             }
37194             
37195             this.xhr.send(formData);
37196             return;
37197         };
37198         
37199         this.uploadCancel();
37200     },
37201     
37202     uploadCancel : function()
37203     {
37204         if (this.xhr) {
37205             this.xhr.abort();
37206         }
37207         
37208         this.delegates = [];
37209         
37210         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37211             el.remove();
37212         }, this);
37213         
37214         this.arrange();
37215     },
37216     
37217     renderPreview : function(file)
37218     {
37219         if(typeof(file.target) != 'undefined' && file.target){
37220             return file;
37221         }
37222         
37223         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37224         
37225         var previewEl = this.managerEl.createChild({
37226             tag : 'div',
37227             cls : 'roo-document-manager-preview',
37228             cn : [
37229                 {
37230                     tag : 'div',
37231                     tooltip : file[this.toolTipName],
37232                     cls : 'roo-document-manager-thumb',
37233                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37234                 },
37235                 {
37236                     tag : 'button',
37237                     cls : 'close',
37238                     html : '<i class="fa fa-times-circle"></i>'
37239                 }
37240             ]
37241         });
37242
37243         var close = previewEl.select('button.close', true).first();
37244
37245         close.on('click', this.onRemove, this, file);
37246
37247         file.target = previewEl;
37248
37249         var image = previewEl.select('img', true).first();
37250         
37251         var _this = this;
37252         
37253         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37254         
37255         image.on('click', this.onClick, this, file);
37256         
37257         this.fireEvent('previewrendered', this, file);
37258         
37259         return file;
37260         
37261     },
37262     
37263     onPreviewLoad : function(file, image)
37264     {
37265         if(typeof(file.target) == 'undefined' || !file.target){
37266             return;
37267         }
37268         
37269         var width = image.dom.naturalWidth || image.dom.width;
37270         var height = image.dom.naturalHeight || image.dom.height;
37271         
37272         if(!this.previewResize) {
37273             return;
37274         }
37275         
37276         if(width > height){
37277             file.target.addClass('wide');
37278             return;
37279         }
37280         
37281         file.target.addClass('tall');
37282         return;
37283         
37284     },
37285     
37286     uploadFromSource : function(file, crop)
37287     {
37288         this.xhr = new XMLHttpRequest();
37289         
37290         this.managerEl.createChild({
37291             tag : 'div',
37292             cls : 'roo-document-manager-loading',
37293             cn : [
37294                 {
37295                     tag : 'div',
37296                     tooltip : file.name,
37297                     cls : 'roo-document-manager-thumb',
37298                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37299                 }
37300             ]
37301
37302         });
37303
37304         this.xhr.open(this.method, this.url, true);
37305         
37306         var headers = {
37307             "Accept": "application/json",
37308             "Cache-Control": "no-cache",
37309             "X-Requested-With": "XMLHttpRequest"
37310         };
37311         
37312         for (var headerName in headers) {
37313             var headerValue = headers[headerName];
37314             if (headerValue) {
37315                 this.xhr.setRequestHeader(headerName, headerValue);
37316             }
37317         }
37318         
37319         var _this = this;
37320         
37321         this.xhr.onload = function()
37322         {
37323             _this.xhrOnLoad(_this.xhr);
37324         }
37325         
37326         this.xhr.onerror = function()
37327         {
37328             _this.xhrOnError(_this.xhr);
37329         }
37330         
37331         var formData = new FormData();
37332
37333         formData.append('returnHTML', 'NO');
37334         
37335         formData.append('crop', crop);
37336         
37337         if(typeof(file.filename) != 'undefined'){
37338             formData.append('filename', file.filename);
37339         }
37340         
37341         if(typeof(file.mimetype) != 'undefined'){
37342             formData.append('mimetype', file.mimetype);
37343         }
37344         
37345         Roo.log(formData);
37346         
37347         if(this.fireEvent('prepare', this, formData) != false){
37348             this.xhr.send(formData);
37349         };
37350     }
37351 });
37352
37353 /*
37354 * Licence: LGPL
37355 */
37356
37357 /**
37358  * @class Roo.bootstrap.DocumentViewer
37359  * @extends Roo.bootstrap.Component
37360  * Bootstrap DocumentViewer class
37361  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37362  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37363  * 
37364  * @constructor
37365  * Create a new DocumentViewer
37366  * @param {Object} config The config object
37367  */
37368
37369 Roo.bootstrap.DocumentViewer = function(config){
37370     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37371     
37372     this.addEvents({
37373         /**
37374          * @event initial
37375          * Fire after initEvent
37376          * @param {Roo.bootstrap.DocumentViewer} this
37377          */
37378         "initial" : true,
37379         /**
37380          * @event click
37381          * Fire after click
37382          * @param {Roo.bootstrap.DocumentViewer} this
37383          */
37384         "click" : true,
37385         /**
37386          * @event download
37387          * Fire after download button
37388          * @param {Roo.bootstrap.DocumentViewer} this
37389          */
37390         "download" : true,
37391         /**
37392          * @event trash
37393          * Fire after trash button
37394          * @param {Roo.bootstrap.DocumentViewer} this
37395          */
37396         "trash" : true
37397         
37398     });
37399 };
37400
37401 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37402     
37403     showDownload : true,
37404     
37405     showTrash : true,
37406     
37407     getAutoCreate : function()
37408     {
37409         var cfg = {
37410             tag : 'div',
37411             cls : 'roo-document-viewer',
37412             cn : [
37413                 {
37414                     tag : 'div',
37415                     cls : 'roo-document-viewer-body',
37416                     cn : [
37417                         {
37418                             tag : 'div',
37419                             cls : 'roo-document-viewer-thumb',
37420                             cn : [
37421                                 {
37422                                     tag : 'img',
37423                                     cls : 'roo-document-viewer-image'
37424                                 }
37425                             ]
37426                         }
37427                     ]
37428                 },
37429                 {
37430                     tag : 'div',
37431                     cls : 'roo-document-viewer-footer',
37432                     cn : {
37433                         tag : 'div',
37434                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37435                         cn : [
37436                             {
37437                                 tag : 'div',
37438                                 cls : 'btn-group roo-document-viewer-download',
37439                                 cn : [
37440                                     {
37441                                         tag : 'button',
37442                                         cls : 'btn btn-default',
37443                                         html : '<i class="fa fa-download"></i>'
37444                                     }
37445                                 ]
37446                             },
37447                             {
37448                                 tag : 'div',
37449                                 cls : 'btn-group roo-document-viewer-trash',
37450                                 cn : [
37451                                     {
37452                                         tag : 'button',
37453                                         cls : 'btn btn-default',
37454                                         html : '<i class="fa fa-trash"></i>'
37455                                     }
37456                                 ]
37457                             }
37458                         ]
37459                     }
37460                 }
37461             ]
37462         };
37463         
37464         return cfg;
37465     },
37466     
37467     initEvents : function()
37468     {
37469         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37470         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37471         
37472         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37473         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37474         
37475         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37476         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37477         
37478         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37479         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37480         
37481         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37482         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37483         
37484         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37485         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37486         
37487         this.bodyEl.on('click', this.onClick, this);
37488         this.downloadBtn.on('click', this.onDownload, this);
37489         this.trashBtn.on('click', this.onTrash, this);
37490         
37491         this.downloadBtn.hide();
37492         this.trashBtn.hide();
37493         
37494         if(this.showDownload){
37495             this.downloadBtn.show();
37496         }
37497         
37498         if(this.showTrash){
37499             this.trashBtn.show();
37500         }
37501         
37502         if(!this.showDownload && !this.showTrash) {
37503             this.footerEl.hide();
37504         }
37505         
37506     },
37507     
37508     initial : function()
37509     {
37510         this.fireEvent('initial', this);
37511         
37512     },
37513     
37514     onClick : function(e)
37515     {
37516         e.preventDefault();
37517         
37518         this.fireEvent('click', this);
37519     },
37520     
37521     onDownload : function(e)
37522     {
37523         e.preventDefault();
37524         
37525         this.fireEvent('download', this);
37526     },
37527     
37528     onTrash : function(e)
37529     {
37530         e.preventDefault();
37531         
37532         this.fireEvent('trash', this);
37533     }
37534     
37535 });
37536 /*
37537  * - LGPL
37538  *
37539  * FieldLabel
37540  * 
37541  */
37542
37543 /**
37544  * @class Roo.bootstrap.form.FieldLabel
37545  * @extends Roo.bootstrap.Component
37546  * Bootstrap FieldLabel class
37547  * @cfg {String} html contents of the element
37548  * @cfg {String} tag tag of the element default label
37549  * @cfg {String} cls class of the element
37550  * @cfg {String} target label target 
37551  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
37552  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
37553  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
37554  * @cfg {String} iconTooltip default "This field is required"
37555  * @cfg {String} indicatorpos (left|right) default left
37556  * 
37557  * @constructor
37558  * Create a new FieldLabel
37559  * @param {Object} config The config object
37560  */
37561
37562 Roo.bootstrap.form.FieldLabel = function(config){
37563     Roo.bootstrap.Element.superclass.constructor.call(this, config);
37564     
37565     this.addEvents({
37566             /**
37567              * @event invalid
37568              * Fires after the field has been marked as invalid.
37569              * @param {Roo.form.FieldLabel} this
37570              * @param {String} msg The validation message
37571              */
37572             invalid : true,
37573             /**
37574              * @event valid
37575              * Fires after the field has been validated with no errors.
37576              * @param {Roo.form.FieldLabel} this
37577              */
37578             valid : true
37579         });
37580 };
37581
37582 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
37583     
37584     tag: 'label',
37585     cls: '',
37586     html: '',
37587     target: '',
37588     allowBlank : true,
37589     invalidClass : 'has-warning',
37590     validClass : 'has-success',
37591     iconTooltip : 'This field is required',
37592     indicatorpos : 'left',
37593     
37594     getAutoCreate : function(){
37595         
37596         var cls = "";
37597         if (!this.allowBlank) {
37598             cls  = "visible";
37599         }
37600         
37601         var cfg = {
37602             tag : this.tag,
37603             cls : 'roo-bootstrap-field-label ' + this.cls,
37604             for : this.target,
37605             cn : [
37606                 {
37607                     tag : 'i',
37608                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
37609                     tooltip : this.iconTooltip
37610                 },
37611                 {
37612                     tag : 'span',
37613                     html : this.html
37614                 }
37615             ] 
37616         };
37617         
37618         if(this.indicatorpos == 'right'){
37619             var cfg = {
37620                 tag : this.tag,
37621                 cls : 'roo-bootstrap-field-label ' + this.cls,
37622                 for : this.target,
37623                 cn : [
37624                     {
37625                         tag : 'span',
37626                         html : this.html
37627                     },
37628                     {
37629                         tag : 'i',
37630                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
37631                         tooltip : this.iconTooltip
37632                     }
37633                 ] 
37634             };
37635         }
37636         
37637         return cfg;
37638     },
37639     
37640     initEvents: function() 
37641     {
37642         Roo.bootstrap.Element.superclass.initEvents.call(this);
37643         
37644         this.indicator = this.indicatorEl();
37645         
37646         if(this.indicator){
37647             this.indicator.removeClass('visible');
37648             this.indicator.addClass('invisible');
37649         }
37650         
37651         Roo.bootstrap.form.FieldLabel.register(this);
37652     },
37653     
37654     indicatorEl : function()
37655     {
37656         var indicator = this.el.select('i.roo-required-indicator',true).first();
37657         
37658         if(!indicator){
37659             return false;
37660         }
37661         
37662         return indicator;
37663         
37664     },
37665     
37666     /**
37667      * Mark this field as valid
37668      */
37669     markValid : function()
37670     {
37671         if(this.indicator){
37672             this.indicator.removeClass('visible');
37673             this.indicator.addClass('invisible');
37674         }
37675         if (Roo.bootstrap.version == 3) {
37676             this.el.removeClass(this.invalidClass);
37677             this.el.addClass(this.validClass);
37678         } else {
37679             this.el.removeClass('is-invalid');
37680             this.el.addClass('is-valid');
37681         }
37682         
37683         
37684         this.fireEvent('valid', this);
37685     },
37686     
37687     /**
37688      * Mark this field as invalid
37689      * @param {String} msg The validation message
37690      */
37691     markInvalid : function(msg)
37692     {
37693         if(this.indicator){
37694             this.indicator.removeClass('invisible');
37695             this.indicator.addClass('visible');
37696         }
37697           if (Roo.bootstrap.version == 3) {
37698             this.el.removeClass(this.validClass);
37699             this.el.addClass(this.invalidClass);
37700         } else {
37701             this.el.removeClass('is-valid');
37702             this.el.addClass('is-invalid');
37703         }
37704         
37705         
37706         this.fireEvent('invalid', this, msg);
37707     }
37708     
37709    
37710 });
37711
37712 Roo.apply(Roo.bootstrap.form.FieldLabel, {
37713     
37714     groups: {},
37715     
37716      /**
37717     * register a FieldLabel Group
37718     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
37719     */
37720     register : function(label)
37721     {
37722         if(this.groups.hasOwnProperty(label.target)){
37723             return;
37724         }
37725      
37726         this.groups[label.target] = label;
37727         
37728     },
37729     /**
37730     * fetch a FieldLabel Group based on the target
37731     * @param {string} target
37732     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
37733     */
37734     get: function(target) {
37735         if (typeof(this.groups[target]) == 'undefined') {
37736             return false;
37737         }
37738         
37739         return this.groups[target] ;
37740     }
37741 });
37742
37743  
37744
37745  /*
37746  * - LGPL
37747  *
37748  * page DateSplitField.
37749  * 
37750  */
37751
37752
37753 /**
37754  * @class Roo.bootstrap.form.DateSplitField
37755  * @extends Roo.bootstrap.Component
37756  * Bootstrap DateSplitField class
37757  * @cfg {string} fieldLabel - the label associated
37758  * @cfg {Number} labelWidth set the width of label (0-12)
37759  * @cfg {String} labelAlign (top|left)
37760  * @cfg {Boolean} dayAllowBlank (true|false) default false
37761  * @cfg {Boolean} monthAllowBlank (true|false) default false
37762  * @cfg {Boolean} yearAllowBlank (true|false) default false
37763  * @cfg {string} dayPlaceholder 
37764  * @cfg {string} monthPlaceholder
37765  * @cfg {string} yearPlaceholder
37766  * @cfg {string} dayFormat default 'd'
37767  * @cfg {string} monthFormat default 'm'
37768  * @cfg {string} yearFormat default 'Y'
37769  * @cfg {Number} labellg set the width of label (1-12)
37770  * @cfg {Number} labelmd set the width of label (1-12)
37771  * @cfg {Number} labelsm set the width of label (1-12)
37772  * @cfg {Number} labelxs set the width of label (1-12)
37773
37774  *     
37775  * @constructor
37776  * Create a new DateSplitField
37777  * @param {Object} config The config object
37778  */
37779
37780 Roo.bootstrap.form.DateSplitField = function(config){
37781     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
37782     
37783     this.addEvents({
37784         // raw events
37785          /**
37786          * @event years
37787          * getting the data of years
37788          * @param {Roo.bootstrap.form.DateSplitField} this
37789          * @param {Object} years
37790          */
37791         "years" : true,
37792         /**
37793          * @event days
37794          * getting the data of days
37795          * @param {Roo.bootstrap.form.DateSplitField} this
37796          * @param {Object} days
37797          */
37798         "days" : true,
37799         /**
37800          * @event invalid
37801          * Fires after the field has been marked as invalid.
37802          * @param {Roo.form.Field} this
37803          * @param {String} msg The validation message
37804          */
37805         invalid : true,
37806        /**
37807          * @event valid
37808          * Fires after the field has been validated with no errors.
37809          * @param {Roo.form.Field} this
37810          */
37811         valid : true
37812     });
37813 };
37814
37815 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
37816     
37817     fieldLabel : '',
37818     labelAlign : 'top',
37819     labelWidth : 3,
37820     dayAllowBlank : false,
37821     monthAllowBlank : false,
37822     yearAllowBlank : false,
37823     dayPlaceholder : '',
37824     monthPlaceholder : '',
37825     yearPlaceholder : '',
37826     dayFormat : 'd',
37827     monthFormat : 'm',
37828     yearFormat : 'Y',
37829     isFormField : true,
37830     labellg : 0,
37831     labelmd : 0,
37832     labelsm : 0,
37833     labelxs : 0,
37834     
37835     getAutoCreate : function()
37836     {
37837         var cfg = {
37838             tag : 'div',
37839             cls : 'row roo-date-split-field-group',
37840             cn : [
37841                 {
37842                     tag : 'input',
37843                     type : 'hidden',
37844                     cls : 'form-hidden-field roo-date-split-field-group-value',
37845                     name : this.name
37846                 }
37847             ]
37848         };
37849         
37850         var labelCls = 'col-md-12';
37851         var contentCls = 'col-md-4';
37852         
37853         if(this.fieldLabel){
37854             
37855             var label = {
37856                 tag : 'div',
37857                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
37858                 cn : [
37859                     {
37860                         tag : 'label',
37861                         html : this.fieldLabel
37862                     }
37863                 ]
37864             };
37865             
37866             if(this.labelAlign == 'left'){
37867             
37868                 if(this.labelWidth > 12){
37869                     label.style = "width: " + this.labelWidth + 'px';
37870                 }
37871
37872                 if(this.labelWidth < 13 && this.labelmd == 0){
37873                     this.labelmd = this.labelWidth;
37874                 }
37875
37876                 if(this.labellg > 0){
37877                     labelCls = ' col-lg-' + this.labellg;
37878                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
37879                 }
37880
37881                 if(this.labelmd > 0){
37882                     labelCls = ' col-md-' + this.labelmd;
37883                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
37884                 }
37885
37886                 if(this.labelsm > 0){
37887                     labelCls = ' col-sm-' + this.labelsm;
37888                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
37889                 }
37890
37891                 if(this.labelxs > 0){
37892                     labelCls = ' col-xs-' + this.labelxs;
37893                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
37894                 }
37895             }
37896             
37897             label.cls += ' ' + labelCls;
37898             
37899             cfg.cn.push(label);
37900         }
37901         
37902         Roo.each(['day', 'month', 'year'], function(t){
37903             cfg.cn.push({
37904                 tag : 'div',
37905                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
37906             });
37907         }, this);
37908         
37909         return cfg;
37910     },
37911     
37912     inputEl: function ()
37913     {
37914         return this.el.select('.roo-date-split-field-group-value', true).first();
37915     },
37916     
37917     onRender : function(ct, position) 
37918     {
37919         var _this = this;
37920         
37921         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
37922         
37923         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
37924         
37925         this.dayField = new Roo.bootstrap.form.ComboBox({
37926             allowBlank : this.dayAllowBlank,
37927             alwaysQuery : true,
37928             displayField : 'value',
37929             editable : false,
37930             fieldLabel : '',
37931             forceSelection : true,
37932             mode : 'local',
37933             placeholder : this.dayPlaceholder,
37934             selectOnFocus : true,
37935             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37936             triggerAction : 'all',
37937             typeAhead : true,
37938             valueField : 'value',
37939             store : new Roo.data.SimpleStore({
37940                 data : (function() {    
37941                     var days = [];
37942                     _this.fireEvent('days', _this, days);
37943                     return days;
37944                 })(),
37945                 fields : [ 'value' ]
37946             }),
37947             listeners : {
37948                 select : function (_self, record, index)
37949                 {
37950                     _this.setValue(_this.getValue());
37951                 }
37952             }
37953         });
37954
37955         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
37956         
37957         this.monthField = new Roo.bootstrap.form.MonthField({
37958             after : '<i class=\"fa fa-calendar\"></i>',
37959             allowBlank : this.monthAllowBlank,
37960             placeholder : this.monthPlaceholder,
37961             readOnly : true,
37962             listeners : {
37963                 render : function (_self)
37964                 {
37965                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
37966                         e.preventDefault();
37967                         _self.focus();
37968                     });
37969                 },
37970                 select : function (_self, oldvalue, newvalue)
37971                 {
37972                     _this.setValue(_this.getValue());
37973                 }
37974             }
37975         });
37976         
37977         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
37978         
37979         this.yearField = new Roo.bootstrap.form.ComboBox({
37980             allowBlank : this.yearAllowBlank,
37981             alwaysQuery : true,
37982             displayField : 'value',
37983             editable : false,
37984             fieldLabel : '',
37985             forceSelection : true,
37986             mode : 'local',
37987             placeholder : this.yearPlaceholder,
37988             selectOnFocus : true,
37989             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37990             triggerAction : 'all',
37991             typeAhead : true,
37992             valueField : 'value',
37993             store : new Roo.data.SimpleStore({
37994                 data : (function() {
37995                     var years = [];
37996                     _this.fireEvent('years', _this, years);
37997                     return years;
37998                 })(),
37999                 fields : [ 'value' ]
38000             }),
38001             listeners : {
38002                 select : function (_self, record, index)
38003                 {
38004                     _this.setValue(_this.getValue());
38005                 }
38006             }
38007         });
38008
38009         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38010     },
38011     
38012     setValue : function(v, format)
38013     {
38014         this.inputEl.dom.value = v;
38015         
38016         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38017         
38018         var d = Date.parseDate(v, f);
38019         
38020         if(!d){
38021             this.validate();
38022             return;
38023         }
38024         
38025         this.setDay(d.format(this.dayFormat));
38026         this.setMonth(d.format(this.monthFormat));
38027         this.setYear(d.format(this.yearFormat));
38028         
38029         this.validate();
38030         
38031         return;
38032     },
38033     
38034     setDay : function(v)
38035     {
38036         this.dayField.setValue(v);
38037         this.inputEl.dom.value = this.getValue();
38038         this.validate();
38039         return;
38040     },
38041     
38042     setMonth : function(v)
38043     {
38044         this.monthField.setValue(v, true);
38045         this.inputEl.dom.value = this.getValue();
38046         this.validate();
38047         return;
38048     },
38049     
38050     setYear : function(v)
38051     {
38052         this.yearField.setValue(v);
38053         this.inputEl.dom.value = this.getValue();
38054         this.validate();
38055         return;
38056     },
38057     
38058     getDay : function()
38059     {
38060         return this.dayField.getValue();
38061     },
38062     
38063     getMonth : function()
38064     {
38065         return this.monthField.getValue();
38066     },
38067     
38068     getYear : function()
38069     {
38070         return this.yearField.getValue();
38071     },
38072     
38073     getValue : function()
38074     {
38075         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38076         
38077         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38078         
38079         return date;
38080     },
38081     
38082     reset : function()
38083     {
38084         this.setDay('');
38085         this.setMonth('');
38086         this.setYear('');
38087         this.inputEl.dom.value = '';
38088         this.validate();
38089         return;
38090     },
38091     
38092     validate : function()
38093     {
38094         var d = this.dayField.validate();
38095         var m = this.monthField.validate();
38096         var y = this.yearField.validate();
38097         
38098         var valid = true;
38099         
38100         if(
38101                 (!this.dayAllowBlank && !d) ||
38102                 (!this.monthAllowBlank && !m) ||
38103                 (!this.yearAllowBlank && !y)
38104         ){
38105             valid = false;
38106         }
38107         
38108         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38109             return valid;
38110         }
38111         
38112         if(valid){
38113             this.markValid();
38114             return valid;
38115         }
38116         
38117         this.markInvalid();
38118         
38119         return valid;
38120     },
38121     
38122     markValid : function()
38123     {
38124         
38125         var label = this.el.select('label', true).first();
38126         var icon = this.el.select('i.fa-star', true).first();
38127
38128         if(label && icon){
38129             icon.remove();
38130         }
38131         
38132         this.fireEvent('valid', this);
38133     },
38134     
38135      /**
38136      * Mark this field as invalid
38137      * @param {String} msg The validation message
38138      */
38139     markInvalid : function(msg)
38140     {
38141         
38142         var label = this.el.select('label', true).first();
38143         var icon = this.el.select('i.fa-star', true).first();
38144
38145         if(label && !icon){
38146             this.el.select('.roo-date-split-field-label', true).createChild({
38147                 tag : 'i',
38148                 cls : 'text-danger fa fa-lg fa-star',
38149                 tooltip : 'This field is required',
38150                 style : 'margin-right:5px;'
38151             }, label, true);
38152         }
38153         
38154         this.fireEvent('invalid', this, msg);
38155     },
38156     
38157     clearInvalid : function()
38158     {
38159         var label = this.el.select('label', true).first();
38160         var icon = this.el.select('i.fa-star', true).first();
38161
38162         if(label && icon){
38163             icon.remove();
38164         }
38165         
38166         this.fireEvent('valid', this);
38167     },
38168     
38169     getName: function()
38170     {
38171         return this.name;
38172     }
38173     
38174 });
38175
38176  
38177
38178 /**
38179  * @class Roo.bootstrap.LayoutMasonry
38180  * @extends Roo.bootstrap.Component
38181  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38182  * Bootstrap Layout Masonry class
38183  *
38184  * This is based on 
38185  * http://masonry.desandro.com
38186  *
38187  * The idea is to render all the bricks based on vertical width...
38188  *
38189  * The original code extends 'outlayer' - we might need to use that....
38190
38191  * @constructor
38192  * Create a new Element
38193  * @param {Object} config The config object
38194  */
38195
38196 Roo.bootstrap.LayoutMasonry = function(config){
38197     
38198     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38199     
38200     this.bricks = [];
38201     
38202     Roo.bootstrap.LayoutMasonry.register(this);
38203     
38204     this.addEvents({
38205         // raw events
38206         /**
38207          * @event layout
38208          * Fire after layout the items
38209          * @param {Roo.bootstrap.LayoutMasonry} this
38210          * @param {Roo.EventObject} e
38211          */
38212         "layout" : true
38213     });
38214     
38215 };
38216
38217 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38218     
38219     /**
38220      * @cfg {Boolean} isLayoutInstant = no animation?
38221      */   
38222     isLayoutInstant : false, // needed?
38223    
38224     /**
38225      * @cfg {Number} boxWidth  width of the columns
38226      */   
38227     boxWidth : 450,
38228     
38229       /**
38230      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38231      */   
38232     boxHeight : 0,
38233     
38234     /**
38235      * @cfg {Number} padWidth padding below box..
38236      */   
38237     padWidth : 10, 
38238     
38239     /**
38240      * @cfg {Number} gutter gutter width..
38241      */   
38242     gutter : 10,
38243     
38244      /**
38245      * @cfg {Number} maxCols maximum number of columns
38246      */   
38247     
38248     maxCols: 0,
38249     
38250     /**
38251      * @cfg {Boolean} isAutoInitial defalut true
38252      */   
38253     isAutoInitial : true, 
38254     
38255     containerWidth: 0,
38256     
38257     /**
38258      * @cfg {Boolean} isHorizontal defalut false
38259      */   
38260     isHorizontal : false, 
38261
38262     currentSize : null,
38263     
38264     tag: 'div',
38265     
38266     cls: '',
38267     
38268     bricks: null, //CompositeElement
38269     
38270     cols : 1,
38271     
38272     _isLayoutInited : false,
38273     
38274 //    isAlternative : false, // only use for vertical layout...
38275     
38276     /**
38277      * @cfg {Number} alternativePadWidth padding below box..
38278      */   
38279     alternativePadWidth : 50,
38280     
38281     selectedBrick : [],
38282     
38283     getAutoCreate : function(){
38284         
38285         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38286         
38287         var cfg = {
38288             tag: this.tag,
38289             cls: 'blog-masonary-wrapper ' + this.cls,
38290             cn : {
38291                 cls : 'mas-boxes masonary'
38292             }
38293         };
38294         
38295         return cfg;
38296     },
38297     
38298     getChildContainer: function( )
38299     {
38300         if (this.boxesEl) {
38301             return this.boxesEl;
38302         }
38303         
38304         this.boxesEl = this.el.select('.mas-boxes').first();
38305         
38306         return this.boxesEl;
38307     },
38308     
38309     
38310     initEvents : function()
38311     {
38312         var _this = this;
38313         
38314         if(this.isAutoInitial){
38315             Roo.log('hook children rendered');
38316             this.on('childrenrendered', function() {
38317                 Roo.log('children rendered');
38318                 _this.initial();
38319             } ,this);
38320         }
38321     },
38322     
38323     initial : function()
38324     {
38325         this.selectedBrick = [];
38326         
38327         this.currentSize = this.el.getBox(true);
38328         
38329         Roo.EventManager.onWindowResize(this.resize, this); 
38330
38331         if(!this.isAutoInitial){
38332             this.layout();
38333             return;
38334         }
38335         
38336         this.layout();
38337         
38338         return;
38339         //this.layout.defer(500,this);
38340         
38341     },
38342     
38343     resize : function()
38344     {
38345         var cs = this.el.getBox(true);
38346         
38347         if (
38348                 this.currentSize.width == cs.width && 
38349                 this.currentSize.x == cs.x && 
38350                 this.currentSize.height == cs.height && 
38351                 this.currentSize.y == cs.y 
38352         ) {
38353             Roo.log("no change in with or X or Y");
38354             return;
38355         }
38356         
38357         this.currentSize = cs;
38358         
38359         this.layout();
38360         
38361     },
38362     
38363     layout : function()
38364     {   
38365         this._resetLayout();
38366         
38367         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38368         
38369         this.layoutItems( isInstant );
38370       
38371         this._isLayoutInited = true;
38372         
38373         this.fireEvent('layout', this);
38374         
38375     },
38376     
38377     _resetLayout : function()
38378     {
38379         if(this.isHorizontal){
38380             this.horizontalMeasureColumns();
38381             return;
38382         }
38383         
38384         this.verticalMeasureColumns();
38385         
38386     },
38387     
38388     verticalMeasureColumns : function()
38389     {
38390         this.getContainerWidth();
38391         
38392 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38393 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38394 //            return;
38395 //        }
38396         
38397         var boxWidth = this.boxWidth + this.padWidth;
38398         
38399         if(this.containerWidth < this.boxWidth){
38400             boxWidth = this.containerWidth
38401         }
38402         
38403         var containerWidth = this.containerWidth;
38404         
38405         var cols = Math.floor(containerWidth / boxWidth);
38406         
38407         this.cols = Math.max( cols, 1 );
38408         
38409         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38410         
38411         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38412         
38413         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38414         
38415         this.colWidth = boxWidth + avail - this.padWidth;
38416         
38417         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38418         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38419     },
38420     
38421     horizontalMeasureColumns : function()
38422     {
38423         this.getContainerWidth();
38424         
38425         var boxWidth = this.boxWidth;
38426         
38427         if(this.containerWidth < boxWidth){
38428             boxWidth = this.containerWidth;
38429         }
38430         
38431         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38432         
38433         this.el.setHeight(boxWidth);
38434         
38435     },
38436     
38437     getContainerWidth : function()
38438     {
38439         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38440     },
38441     
38442     layoutItems : function( isInstant )
38443     {
38444         Roo.log(this.bricks);
38445         
38446         var items = Roo.apply([], this.bricks);
38447         
38448         if(this.isHorizontal){
38449             this._horizontalLayoutItems( items , isInstant );
38450             return;
38451         }
38452         
38453 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38454 //            this._verticalAlternativeLayoutItems( items , isInstant );
38455 //            return;
38456 //        }
38457         
38458         this._verticalLayoutItems( items , isInstant );
38459         
38460     },
38461     
38462     _verticalLayoutItems : function ( items , isInstant)
38463     {
38464         if ( !items || !items.length ) {
38465             return;
38466         }
38467         
38468         var standard = [
38469             ['xs', 'xs', 'xs', 'tall'],
38470             ['xs', 'xs', 'tall'],
38471             ['xs', 'xs', 'sm'],
38472             ['xs', 'xs', 'xs'],
38473             ['xs', 'tall'],
38474             ['xs', 'sm'],
38475             ['xs', 'xs'],
38476             ['xs'],
38477             
38478             ['sm', 'xs', 'xs'],
38479             ['sm', 'xs'],
38480             ['sm'],
38481             
38482             ['tall', 'xs', 'xs', 'xs'],
38483             ['tall', 'xs', 'xs'],
38484             ['tall', 'xs'],
38485             ['tall']
38486             
38487         ];
38488         
38489         var queue = [];
38490         
38491         var boxes = [];
38492         
38493         var box = [];
38494         
38495         Roo.each(items, function(item, k){
38496             
38497             switch (item.size) {
38498                 // these layouts take up a full box,
38499                 case 'md' :
38500                 case 'md-left' :
38501                 case 'md-right' :
38502                 case 'wide' :
38503                     
38504                     if(box.length){
38505                         boxes.push(box);
38506                         box = [];
38507                     }
38508                     
38509                     boxes.push([item]);
38510                     
38511                     break;
38512                     
38513                 case 'xs' :
38514                 case 'sm' :
38515                 case 'tall' :
38516                     
38517                     box.push(item);
38518                     
38519                     break;
38520                 default :
38521                     break;
38522                     
38523             }
38524             
38525         }, this);
38526         
38527         if(box.length){
38528             boxes.push(box);
38529             box = [];
38530         }
38531         
38532         var filterPattern = function(box, length)
38533         {
38534             if(!box.length){
38535                 return;
38536             }
38537             
38538             var match = false;
38539             
38540             var pattern = box.slice(0, length);
38541             
38542             var format = [];
38543             
38544             Roo.each(pattern, function(i){
38545                 format.push(i.size);
38546             }, this);
38547             
38548             Roo.each(standard, function(s){
38549                 
38550                 if(String(s) != String(format)){
38551                     return;
38552                 }
38553                 
38554                 match = true;
38555                 return false;
38556                 
38557             }, this);
38558             
38559             if(!match && length == 1){
38560                 return;
38561             }
38562             
38563             if(!match){
38564                 filterPattern(box, length - 1);
38565                 return;
38566             }
38567                 
38568             queue.push(pattern);
38569
38570             box = box.slice(length, box.length);
38571
38572             filterPattern(box, 4);
38573
38574             return;
38575             
38576         }
38577         
38578         Roo.each(boxes, function(box, k){
38579             
38580             if(!box.length){
38581                 return;
38582             }
38583             
38584             if(box.length == 1){
38585                 queue.push(box);
38586                 return;
38587             }
38588             
38589             filterPattern(box, 4);
38590             
38591         }, this);
38592         
38593         this._processVerticalLayoutQueue( queue, isInstant );
38594         
38595     },
38596     
38597 //    _verticalAlternativeLayoutItems : function( items , isInstant )
38598 //    {
38599 //        if ( !items || !items.length ) {
38600 //            return;
38601 //        }
38602 //
38603 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
38604 //        
38605 //    },
38606     
38607     _horizontalLayoutItems : function ( items , isInstant)
38608     {
38609         if ( !items || !items.length || items.length < 3) {
38610             return;
38611         }
38612         
38613         items.reverse();
38614         
38615         var eItems = items.slice(0, 3);
38616         
38617         items = items.slice(3, items.length);
38618         
38619         var standard = [
38620             ['xs', 'xs', 'xs', 'wide'],
38621             ['xs', 'xs', 'wide'],
38622             ['xs', 'xs', 'sm'],
38623             ['xs', 'xs', 'xs'],
38624             ['xs', 'wide'],
38625             ['xs', 'sm'],
38626             ['xs', 'xs'],
38627             ['xs'],
38628             
38629             ['sm', 'xs', 'xs'],
38630             ['sm', 'xs'],
38631             ['sm'],
38632             
38633             ['wide', 'xs', 'xs', 'xs'],
38634             ['wide', 'xs', 'xs'],
38635             ['wide', 'xs'],
38636             ['wide'],
38637             
38638             ['wide-thin']
38639         ];
38640         
38641         var queue = [];
38642         
38643         var boxes = [];
38644         
38645         var box = [];
38646         
38647         Roo.each(items, function(item, k){
38648             
38649             switch (item.size) {
38650                 case 'md' :
38651                 case 'md-left' :
38652                 case 'md-right' :
38653                 case 'tall' :
38654                     
38655                     if(box.length){
38656                         boxes.push(box);
38657                         box = [];
38658                     }
38659                     
38660                     boxes.push([item]);
38661                     
38662                     break;
38663                     
38664                 case 'xs' :
38665                 case 'sm' :
38666                 case 'wide' :
38667                 case 'wide-thin' :
38668                     
38669                     box.push(item);
38670                     
38671                     break;
38672                 default :
38673                     break;
38674                     
38675             }
38676             
38677         }, this);
38678         
38679         if(box.length){
38680             boxes.push(box);
38681             box = [];
38682         }
38683         
38684         var filterPattern = function(box, length)
38685         {
38686             if(!box.length){
38687                 return;
38688             }
38689             
38690             var match = false;
38691             
38692             var pattern = box.slice(0, length);
38693             
38694             var format = [];
38695             
38696             Roo.each(pattern, function(i){
38697                 format.push(i.size);
38698             }, this);
38699             
38700             Roo.each(standard, function(s){
38701                 
38702                 if(String(s) != String(format)){
38703                     return;
38704                 }
38705                 
38706                 match = true;
38707                 return false;
38708                 
38709             }, this);
38710             
38711             if(!match && length == 1){
38712                 return;
38713             }
38714             
38715             if(!match){
38716                 filterPattern(box, length - 1);
38717                 return;
38718             }
38719                 
38720             queue.push(pattern);
38721
38722             box = box.slice(length, box.length);
38723
38724             filterPattern(box, 4);
38725
38726             return;
38727             
38728         }
38729         
38730         Roo.each(boxes, function(box, k){
38731             
38732             if(!box.length){
38733                 return;
38734             }
38735             
38736             if(box.length == 1){
38737                 queue.push(box);
38738                 return;
38739             }
38740             
38741             filterPattern(box, 4);
38742             
38743         }, this);
38744         
38745         
38746         var prune = [];
38747         
38748         var pos = this.el.getBox(true);
38749         
38750         var minX = pos.x;
38751         
38752         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38753         
38754         var hit_end = false;
38755         
38756         Roo.each(queue, function(box){
38757             
38758             if(hit_end){
38759                 
38760                 Roo.each(box, function(b){
38761                 
38762                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38763                     b.el.hide();
38764
38765                 }, this);
38766
38767                 return;
38768             }
38769             
38770             var mx = 0;
38771             
38772             Roo.each(box, function(b){
38773                 
38774                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38775                 b.el.show();
38776
38777                 mx = Math.max(mx, b.x);
38778                 
38779             }, this);
38780             
38781             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
38782             
38783             if(maxX < minX){
38784                 
38785                 Roo.each(box, function(b){
38786                 
38787                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38788                     b.el.hide();
38789                     
38790                 }, this);
38791                 
38792                 hit_end = true;
38793                 
38794                 return;
38795             }
38796             
38797             prune.push(box);
38798             
38799         }, this);
38800         
38801         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
38802     },
38803     
38804     /** Sets position of item in DOM
38805     * @param {Element} item
38806     * @param {Number} x - horizontal position
38807     * @param {Number} y - vertical position
38808     * @param {Boolean} isInstant - disables transitions
38809     */
38810     _processVerticalLayoutQueue : function( queue, isInstant )
38811     {
38812         var pos = this.el.getBox(true);
38813         var x = pos.x;
38814         var y = pos.y;
38815         var maxY = [];
38816         
38817         for (var i = 0; i < this.cols; i++){
38818             maxY[i] = pos.y;
38819         }
38820         
38821         Roo.each(queue, function(box, k){
38822             
38823             var col = k % this.cols;
38824             
38825             Roo.each(box, function(b,kk){
38826                 
38827                 b.el.position('absolute');
38828                 
38829                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38830                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38831                 
38832                 if(b.size == 'md-left' || b.size == 'md-right'){
38833                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38834                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38835                 }
38836                 
38837                 b.el.setWidth(width);
38838                 b.el.setHeight(height);
38839                 // iframe?
38840                 b.el.select('iframe',true).setSize(width,height);
38841                 
38842             }, this);
38843             
38844             for (var i = 0; i < this.cols; i++){
38845                 
38846                 if(maxY[i] < maxY[col]){
38847                     col = i;
38848                     continue;
38849                 }
38850                 
38851                 col = Math.min(col, i);
38852                 
38853             }
38854             
38855             x = pos.x + col * (this.colWidth + this.padWidth);
38856             
38857             y = maxY[col];
38858             
38859             var positions = [];
38860             
38861             switch (box.length){
38862                 case 1 :
38863                     positions = this.getVerticalOneBoxColPositions(x, y, box);
38864                     break;
38865                 case 2 :
38866                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
38867                     break;
38868                 case 3 :
38869                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
38870                     break;
38871                 case 4 :
38872                     positions = this.getVerticalFourBoxColPositions(x, y, box);
38873                     break;
38874                 default :
38875                     break;
38876             }
38877             
38878             Roo.each(box, function(b,kk){
38879                 
38880                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38881                 
38882                 var sz = b.el.getSize();
38883                 
38884                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
38885                 
38886             }, this);
38887             
38888         }, this);
38889         
38890         var mY = 0;
38891         
38892         for (var i = 0; i < this.cols; i++){
38893             mY = Math.max(mY, maxY[i]);
38894         }
38895         
38896         this.el.setHeight(mY - pos.y);
38897         
38898     },
38899     
38900 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
38901 //    {
38902 //        var pos = this.el.getBox(true);
38903 //        var x = pos.x;
38904 //        var y = pos.y;
38905 //        var maxX = pos.right;
38906 //        
38907 //        var maxHeight = 0;
38908 //        
38909 //        Roo.each(items, function(item, k){
38910 //            
38911 //            var c = k % 2;
38912 //            
38913 //            item.el.position('absolute');
38914 //                
38915 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
38916 //
38917 //            item.el.setWidth(width);
38918 //
38919 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
38920 //
38921 //            item.el.setHeight(height);
38922 //            
38923 //            if(c == 0){
38924 //                item.el.setXY([x, y], isInstant ? false : true);
38925 //            } else {
38926 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
38927 //            }
38928 //            
38929 //            y = y + height + this.alternativePadWidth;
38930 //            
38931 //            maxHeight = maxHeight + height + this.alternativePadWidth;
38932 //            
38933 //        }, this);
38934 //        
38935 //        this.el.setHeight(maxHeight);
38936 //        
38937 //    },
38938     
38939     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
38940     {
38941         var pos = this.el.getBox(true);
38942         
38943         var minX = pos.x;
38944         var minY = pos.y;
38945         
38946         var maxX = pos.right;
38947         
38948         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
38949         
38950         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38951         
38952         Roo.each(queue, function(box, k){
38953             
38954             Roo.each(box, function(b, kk){
38955                 
38956                 b.el.position('absolute');
38957                 
38958                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38959                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38960                 
38961                 if(b.size == 'md-left' || b.size == 'md-right'){
38962                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38963                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38964                 }
38965                 
38966                 b.el.setWidth(width);
38967                 b.el.setHeight(height);
38968                 
38969             }, this);
38970             
38971             if(!box.length){
38972                 return;
38973             }
38974             
38975             var positions = [];
38976             
38977             switch (box.length){
38978                 case 1 :
38979                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
38980                     break;
38981                 case 2 :
38982                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
38983                     break;
38984                 case 3 :
38985                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
38986                     break;
38987                 case 4 :
38988                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
38989                     break;
38990                 default :
38991                     break;
38992             }
38993             
38994             Roo.each(box, function(b,kk){
38995                 
38996                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38997                 
38998                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
38999                 
39000             }, this);
39001             
39002         }, this);
39003         
39004     },
39005     
39006     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39007     {
39008         Roo.each(eItems, function(b,k){
39009             
39010             b.size = (k == 0) ? 'sm' : 'xs';
39011             b.x = (k == 0) ? 2 : 1;
39012             b.y = (k == 0) ? 2 : 1;
39013             
39014             b.el.position('absolute');
39015             
39016             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39017                 
39018             b.el.setWidth(width);
39019             
39020             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39021             
39022             b.el.setHeight(height);
39023             
39024         }, this);
39025
39026         var positions = [];
39027         
39028         positions.push({
39029             x : maxX - this.unitWidth * 2 - this.gutter,
39030             y : minY
39031         });
39032         
39033         positions.push({
39034             x : maxX - this.unitWidth,
39035             y : minY + (this.unitWidth + this.gutter) * 2
39036         });
39037         
39038         positions.push({
39039             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39040             y : minY
39041         });
39042         
39043         Roo.each(eItems, function(b,k){
39044             
39045             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39046
39047         }, this);
39048         
39049     },
39050     
39051     getVerticalOneBoxColPositions : function(x, y, box)
39052     {
39053         var pos = [];
39054         
39055         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39056         
39057         if(box[0].size == 'md-left'){
39058             rand = 0;
39059         }
39060         
39061         if(box[0].size == 'md-right'){
39062             rand = 1;
39063         }
39064         
39065         pos.push({
39066             x : x + (this.unitWidth + this.gutter) * rand,
39067             y : y
39068         });
39069         
39070         return pos;
39071     },
39072     
39073     getVerticalTwoBoxColPositions : function(x, y, box)
39074     {
39075         var pos = [];
39076         
39077         if(box[0].size == 'xs'){
39078             
39079             pos.push({
39080                 x : x,
39081                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39082             });
39083
39084             pos.push({
39085                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39086                 y : y
39087             });
39088             
39089             return pos;
39090             
39091         }
39092         
39093         pos.push({
39094             x : x,
39095             y : y
39096         });
39097
39098         pos.push({
39099             x : x + (this.unitWidth + this.gutter) * 2,
39100             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39101         });
39102         
39103         return pos;
39104         
39105     },
39106     
39107     getVerticalThreeBoxColPositions : function(x, y, box)
39108     {
39109         var pos = [];
39110         
39111         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39112             
39113             pos.push({
39114                 x : x,
39115                 y : y
39116             });
39117
39118             pos.push({
39119                 x : x + (this.unitWidth + this.gutter) * 1,
39120                 y : y
39121             });
39122             
39123             pos.push({
39124                 x : x + (this.unitWidth + this.gutter) * 2,
39125                 y : y
39126             });
39127             
39128             return pos;
39129             
39130         }
39131         
39132         if(box[0].size == 'xs' && box[1].size == 'xs'){
39133             
39134             pos.push({
39135                 x : x,
39136                 y : y
39137             });
39138
39139             pos.push({
39140                 x : x,
39141                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39142             });
39143             
39144             pos.push({
39145                 x : x + (this.unitWidth + this.gutter) * 1,
39146                 y : y
39147             });
39148             
39149             return pos;
39150             
39151         }
39152         
39153         pos.push({
39154             x : x,
39155             y : y
39156         });
39157
39158         pos.push({
39159             x : x + (this.unitWidth + this.gutter) * 2,
39160             y : y
39161         });
39162
39163         pos.push({
39164             x : x + (this.unitWidth + this.gutter) * 2,
39165             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39166         });
39167             
39168         return pos;
39169         
39170     },
39171     
39172     getVerticalFourBoxColPositions : function(x, y, box)
39173     {
39174         var pos = [];
39175         
39176         if(box[0].size == 'xs'){
39177             
39178             pos.push({
39179                 x : x,
39180                 y : y
39181             });
39182
39183             pos.push({
39184                 x : x,
39185                 y : y + (this.unitHeight + this.gutter) * 1
39186             });
39187             
39188             pos.push({
39189                 x : x,
39190                 y : y + (this.unitHeight + this.gutter) * 2
39191             });
39192             
39193             pos.push({
39194                 x : x + (this.unitWidth + this.gutter) * 1,
39195                 y : y
39196             });
39197             
39198             return pos;
39199             
39200         }
39201         
39202         pos.push({
39203             x : x,
39204             y : y
39205         });
39206
39207         pos.push({
39208             x : x + (this.unitWidth + this.gutter) * 2,
39209             y : y
39210         });
39211
39212         pos.push({
39213             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39214             y : y + (this.unitHeight + this.gutter) * 1
39215         });
39216
39217         pos.push({
39218             x : x + (this.unitWidth + this.gutter) * 2,
39219             y : y + (this.unitWidth + this.gutter) * 2
39220         });
39221
39222         return pos;
39223         
39224     },
39225     
39226     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39227     {
39228         var pos = [];
39229         
39230         if(box[0].size == 'md-left'){
39231             pos.push({
39232                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39233                 y : minY
39234             });
39235             
39236             return pos;
39237         }
39238         
39239         if(box[0].size == 'md-right'){
39240             pos.push({
39241                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39242                 y : minY + (this.unitWidth + this.gutter) * 1
39243             });
39244             
39245             return pos;
39246         }
39247         
39248         var rand = Math.floor(Math.random() * (4 - box[0].y));
39249         
39250         pos.push({
39251             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39252             y : minY + (this.unitWidth + this.gutter) * rand
39253         });
39254         
39255         return pos;
39256         
39257     },
39258     
39259     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39260     {
39261         var pos = [];
39262         
39263         if(box[0].size == 'xs'){
39264             
39265             pos.push({
39266                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39267                 y : minY
39268             });
39269
39270             pos.push({
39271                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39272                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39273             });
39274             
39275             return pos;
39276             
39277         }
39278         
39279         pos.push({
39280             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39281             y : minY
39282         });
39283
39284         pos.push({
39285             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39286             y : minY + (this.unitWidth + this.gutter) * 2
39287         });
39288         
39289         return pos;
39290         
39291     },
39292     
39293     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39294     {
39295         var pos = [];
39296         
39297         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39298             
39299             pos.push({
39300                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39301                 y : minY
39302             });
39303
39304             pos.push({
39305                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39306                 y : minY + (this.unitWidth + this.gutter) * 1
39307             });
39308             
39309             pos.push({
39310                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39311                 y : minY + (this.unitWidth + this.gutter) * 2
39312             });
39313             
39314             return pos;
39315             
39316         }
39317         
39318         if(box[0].size == 'xs' && box[1].size == 'xs'){
39319             
39320             pos.push({
39321                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39322                 y : minY
39323             });
39324
39325             pos.push({
39326                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39327                 y : minY
39328             });
39329             
39330             pos.push({
39331                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39332                 y : minY + (this.unitWidth + this.gutter) * 1
39333             });
39334             
39335             return pos;
39336             
39337         }
39338         
39339         pos.push({
39340             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39341             y : minY
39342         });
39343
39344         pos.push({
39345             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39346             y : minY + (this.unitWidth + this.gutter) * 2
39347         });
39348
39349         pos.push({
39350             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39351             y : minY + (this.unitWidth + this.gutter) * 2
39352         });
39353             
39354         return pos;
39355         
39356     },
39357     
39358     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39359     {
39360         var pos = [];
39361         
39362         if(box[0].size == 'xs'){
39363             
39364             pos.push({
39365                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39366                 y : minY
39367             });
39368
39369             pos.push({
39370                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39371                 y : minY
39372             });
39373             
39374             pos.push({
39375                 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),
39376                 y : minY
39377             });
39378             
39379             pos.push({
39380                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39381                 y : minY + (this.unitWidth + this.gutter) * 1
39382             });
39383             
39384             return pos;
39385             
39386         }
39387         
39388         pos.push({
39389             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39390             y : minY
39391         });
39392         
39393         pos.push({
39394             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39395             y : minY + (this.unitWidth + this.gutter) * 2
39396         });
39397         
39398         pos.push({
39399             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39400             y : minY + (this.unitWidth + this.gutter) * 2
39401         });
39402         
39403         pos.push({
39404             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),
39405             y : minY + (this.unitWidth + this.gutter) * 2
39406         });
39407
39408         return pos;
39409         
39410     },
39411     
39412     /**
39413     * remove a Masonry Brick
39414     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39415     */
39416     removeBrick : function(brick_id)
39417     {
39418         if (!brick_id) {
39419             return;
39420         }
39421         
39422         for (var i = 0; i<this.bricks.length; i++) {
39423             if (this.bricks[i].id == brick_id) {
39424                 this.bricks.splice(i,1);
39425                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39426                 this.initial();
39427             }
39428         }
39429     },
39430     
39431     /**
39432     * adds a Masonry Brick
39433     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39434     */
39435     addBrick : function(cfg)
39436     {
39437         var cn = new Roo.bootstrap.MasonryBrick(cfg);
39438         //this.register(cn);
39439         cn.parentId = this.id;
39440         cn.render(this.el);
39441         return cn;
39442     },
39443     
39444     /**
39445     * register a Masonry Brick
39446     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39447     */
39448     
39449     register : function(brick)
39450     {
39451         this.bricks.push(brick);
39452         brick.masonryId = this.id;
39453     },
39454     
39455     /**
39456     * clear all the Masonry Brick
39457     */
39458     clearAll : function()
39459     {
39460         this.bricks = [];
39461         //this.getChildContainer().dom.innerHTML = "";
39462         this.el.dom.innerHTML = '';
39463     },
39464     
39465     getSelected : function()
39466     {
39467         if (!this.selectedBrick) {
39468             return false;
39469         }
39470         
39471         return this.selectedBrick;
39472     }
39473 });
39474
39475 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39476     
39477     groups: {},
39478      /**
39479     * register a Masonry Layout
39480     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39481     */
39482     
39483     register : function(layout)
39484     {
39485         this.groups[layout.id] = layout;
39486     },
39487     /**
39488     * fetch a  Masonry Layout based on the masonry layout ID
39489     * @param {string} the masonry layout to add
39490     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39491     */
39492     
39493     get: function(layout_id) {
39494         if (typeof(this.groups[layout_id]) == 'undefined') {
39495             return false;
39496         }
39497         return this.groups[layout_id] ;
39498     }
39499     
39500     
39501     
39502 });
39503
39504  
39505
39506  /**
39507  *
39508  * This is based on 
39509  * http://masonry.desandro.com
39510  *
39511  * The idea is to render all the bricks based on vertical width...
39512  *
39513  * The original code extends 'outlayer' - we might need to use that....
39514  * 
39515  */
39516
39517
39518 /**
39519  * @class Roo.bootstrap.LayoutMasonryAuto
39520  * @extends Roo.bootstrap.Component
39521  * Bootstrap Layout Masonry class
39522  * 
39523  * @constructor
39524  * Create a new Element
39525  * @param {Object} config The config object
39526  */
39527
39528 Roo.bootstrap.LayoutMasonryAuto = function(config){
39529     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39530 };
39531
39532 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
39533     
39534       /**
39535      * @cfg {Boolean} isFitWidth  - resize the width..
39536      */   
39537     isFitWidth : false,  // options..
39538     /**
39539      * @cfg {Boolean} isOriginLeft = left align?
39540      */   
39541     isOriginLeft : true,
39542     /**
39543      * @cfg {Boolean} isOriginTop = top align?
39544      */   
39545     isOriginTop : false,
39546     /**
39547      * @cfg {Boolean} isLayoutInstant = no animation?
39548      */   
39549     isLayoutInstant : false, // needed?
39550     /**
39551      * @cfg {Boolean} isResizingContainer = not sure if this is used..
39552      */   
39553     isResizingContainer : true,
39554     /**
39555      * @cfg {Number} columnWidth  width of the columns 
39556      */   
39557     
39558     columnWidth : 0,
39559     
39560     /**
39561      * @cfg {Number} maxCols maximum number of columns
39562      */   
39563     
39564     maxCols: 0,
39565     /**
39566      * @cfg {Number} padHeight padding below box..
39567      */   
39568     
39569     padHeight : 10, 
39570     
39571     /**
39572      * @cfg {Boolean} isAutoInitial defalut true
39573      */   
39574     
39575     isAutoInitial : true, 
39576     
39577     // private?
39578     gutter : 0,
39579     
39580     containerWidth: 0,
39581     initialColumnWidth : 0,
39582     currentSize : null,
39583     
39584     colYs : null, // array.
39585     maxY : 0,
39586     padWidth: 10,
39587     
39588     
39589     tag: 'div',
39590     cls: '',
39591     bricks: null, //CompositeElement
39592     cols : 0, // array?
39593     // element : null, // wrapped now this.el
39594     _isLayoutInited : null, 
39595     
39596     
39597     getAutoCreate : function(){
39598         
39599         var cfg = {
39600             tag: this.tag,
39601             cls: 'blog-masonary-wrapper ' + this.cls,
39602             cn : {
39603                 cls : 'mas-boxes masonary'
39604             }
39605         };
39606         
39607         return cfg;
39608     },
39609     
39610     getChildContainer: function( )
39611     {
39612         if (this.boxesEl) {
39613             return this.boxesEl;
39614         }
39615         
39616         this.boxesEl = this.el.select('.mas-boxes').first();
39617         
39618         return this.boxesEl;
39619     },
39620     
39621     
39622     initEvents : function()
39623     {
39624         var _this = this;
39625         
39626         if(this.isAutoInitial){
39627             Roo.log('hook children rendered');
39628             this.on('childrenrendered', function() {
39629                 Roo.log('children rendered');
39630                 _this.initial();
39631             } ,this);
39632         }
39633         
39634     },
39635     
39636     initial : function()
39637     {
39638         this.reloadItems();
39639
39640         this.currentSize = this.el.getBox(true);
39641
39642         /// was window resize... - let's see if this works..
39643         Roo.EventManager.onWindowResize(this.resize, this); 
39644
39645         if(!this.isAutoInitial){
39646             this.layout();
39647             return;
39648         }
39649         
39650         this.layout.defer(500,this);
39651     },
39652     
39653     reloadItems: function()
39654     {
39655         this.bricks = this.el.select('.masonry-brick', true);
39656         
39657         this.bricks.each(function(b) {
39658             //Roo.log(b.getSize());
39659             if (!b.attr('originalwidth')) {
39660                 b.attr('originalwidth',  b.getSize().width);
39661             }
39662             
39663         });
39664         
39665         Roo.log(this.bricks.elements.length);
39666     },
39667     
39668     resize : function()
39669     {
39670         Roo.log('resize');
39671         var cs = this.el.getBox(true);
39672         
39673         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
39674             Roo.log("no change in with or X");
39675             return;
39676         }
39677         this.currentSize = cs;
39678         this.layout();
39679     },
39680     
39681     layout : function()
39682     {
39683          Roo.log('layout');
39684         this._resetLayout();
39685         //this._manageStamps();
39686       
39687         // don't animate first layout
39688         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39689         this.layoutItems( isInstant );
39690       
39691         // flag for initalized
39692         this._isLayoutInited = true;
39693     },
39694     
39695     layoutItems : function( isInstant )
39696     {
39697         //var items = this._getItemsForLayout( this.items );
39698         // original code supports filtering layout items.. we just ignore it..
39699         
39700         this._layoutItems( this.bricks , isInstant );
39701       
39702         this._postLayout();
39703     },
39704     _layoutItems : function ( items , isInstant)
39705     {
39706        //this.fireEvent( 'layout', this, items );
39707     
39708
39709         if ( !items || !items.elements.length ) {
39710           // no items, emit event with empty array
39711             return;
39712         }
39713
39714         var queue = [];
39715         items.each(function(item) {
39716             Roo.log("layout item");
39717             Roo.log(item);
39718             // get x/y object from method
39719             var position = this._getItemLayoutPosition( item );
39720             // enqueue
39721             position.item = item;
39722             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
39723             queue.push( position );
39724         }, this);
39725       
39726         this._processLayoutQueue( queue );
39727     },
39728     /** Sets position of item in DOM
39729     * @param {Element} item
39730     * @param {Number} x - horizontal position
39731     * @param {Number} y - vertical position
39732     * @param {Boolean} isInstant - disables transitions
39733     */
39734     _processLayoutQueue : function( queue )
39735     {
39736         for ( var i=0, len = queue.length; i < len; i++ ) {
39737             var obj = queue[i];
39738             obj.item.position('absolute');
39739             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
39740         }
39741     },
39742       
39743     
39744     /**
39745     * Any logic you want to do after each layout,
39746     * i.e. size the container
39747     */
39748     _postLayout : function()
39749     {
39750         this.resizeContainer();
39751     },
39752     
39753     resizeContainer : function()
39754     {
39755         if ( !this.isResizingContainer ) {
39756             return;
39757         }
39758         var size = this._getContainerSize();
39759         if ( size ) {
39760             this.el.setSize(size.width,size.height);
39761             this.boxesEl.setSize(size.width,size.height);
39762         }
39763     },
39764     
39765     
39766     
39767     _resetLayout : function()
39768     {
39769         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
39770         this.colWidth = this.el.getWidth();
39771         //this.gutter = this.el.getWidth(); 
39772         
39773         this.measureColumns();
39774
39775         // reset column Y
39776         var i = this.cols;
39777         this.colYs = [];
39778         while (i--) {
39779             this.colYs.push( 0 );
39780         }
39781     
39782         this.maxY = 0;
39783     },
39784
39785     measureColumns : function()
39786     {
39787         this.getContainerWidth();
39788       // if columnWidth is 0, default to outerWidth of first item
39789         if ( !this.columnWidth ) {
39790             var firstItem = this.bricks.first();
39791             Roo.log(firstItem);
39792             this.columnWidth  = this.containerWidth;
39793             if (firstItem && firstItem.attr('originalwidth') ) {
39794                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
39795             }
39796             // columnWidth fall back to item of first element
39797             Roo.log("set column width?");
39798                         this.initialColumnWidth = this.columnWidth  ;
39799
39800             // if first elem has no width, default to size of container
39801             
39802         }
39803         
39804         
39805         if (this.initialColumnWidth) {
39806             this.columnWidth = this.initialColumnWidth;
39807         }
39808         
39809         
39810             
39811         // column width is fixed at the top - however if container width get's smaller we should
39812         // reduce it...
39813         
39814         // this bit calcs how man columns..
39815             
39816         var columnWidth = this.columnWidth += this.gutter;
39817       
39818         // calculate columns
39819         var containerWidth = this.containerWidth + this.gutter;
39820         
39821         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
39822         // fix rounding errors, typically with gutters
39823         var excess = columnWidth - containerWidth % columnWidth;
39824         
39825         
39826         // if overshoot is less than a pixel, round up, otherwise floor it
39827         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
39828         cols = Math[ mathMethod ]( cols );
39829         this.cols = Math.max( cols, 1 );
39830         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39831         
39832          // padding positioning..
39833         var totalColWidth = this.cols * this.columnWidth;
39834         var padavail = this.containerWidth - totalColWidth;
39835         // so for 2 columns - we need 3 'pads'
39836         
39837         var padNeeded = (1+this.cols) * this.padWidth;
39838         
39839         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
39840         
39841         this.columnWidth += padExtra
39842         //this.padWidth = Math.floor(padavail /  ( this.cols));
39843         
39844         // adjust colum width so that padding is fixed??
39845         
39846         // we have 3 columns ... total = width * 3
39847         // we have X left over... that should be used by 
39848         
39849         //if (this.expandC) {
39850             
39851         //}
39852         
39853         
39854         
39855     },
39856     
39857     getContainerWidth : function()
39858     {
39859        /* // container is parent if fit width
39860         var container = this.isFitWidth ? this.element.parentNode : this.element;
39861         // check that this.size and size are there
39862         // IE8 triggers resize on body size change, so they might not be
39863         
39864         var size = getSize( container );  //FIXME
39865         this.containerWidth = size && size.innerWidth; //FIXME
39866         */
39867          
39868         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39869         
39870     },
39871     
39872     _getItemLayoutPosition : function( item )  // what is item?
39873     {
39874         // we resize the item to our columnWidth..
39875       
39876         item.setWidth(this.columnWidth);
39877         item.autoBoxAdjust  = false;
39878         
39879         var sz = item.getSize();
39880  
39881         // how many columns does this brick span
39882         var remainder = this.containerWidth % this.columnWidth;
39883         
39884         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
39885         // round if off by 1 pixel, otherwise use ceil
39886         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
39887         colSpan = Math.min( colSpan, this.cols );
39888         
39889         // normally this should be '1' as we dont' currently allow multi width columns..
39890         
39891         var colGroup = this._getColGroup( colSpan );
39892         // get the minimum Y value from the columns
39893         var minimumY = Math.min.apply( Math, colGroup );
39894         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39895         
39896         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
39897          
39898         // position the brick
39899         var position = {
39900             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
39901             y: this.currentSize.y + minimumY + this.padHeight
39902         };
39903         
39904         Roo.log(position);
39905         // apply setHeight to necessary columns
39906         var setHeight = minimumY + sz.height + this.padHeight;
39907         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39908         
39909         var setSpan = this.cols + 1 - colGroup.length;
39910         for ( var i = 0; i < setSpan; i++ ) {
39911           this.colYs[ shortColIndex + i ] = setHeight ;
39912         }
39913       
39914         return position;
39915     },
39916     
39917     /**
39918      * @param {Number} colSpan - number of columns the element spans
39919      * @returns {Array} colGroup
39920      */
39921     _getColGroup : function( colSpan )
39922     {
39923         if ( colSpan < 2 ) {
39924           // if brick spans only one column, use all the column Ys
39925           return this.colYs;
39926         }
39927       
39928         var colGroup = [];
39929         // how many different places could this brick fit horizontally
39930         var groupCount = this.cols + 1 - colSpan;
39931         // for each group potential horizontal position
39932         for ( var i = 0; i < groupCount; i++ ) {
39933           // make an array of colY values for that one group
39934           var groupColYs = this.colYs.slice( i, i + colSpan );
39935           // and get the max value of the array
39936           colGroup[i] = Math.max.apply( Math, groupColYs );
39937         }
39938         return colGroup;
39939     },
39940     /*
39941     _manageStamp : function( stamp )
39942     {
39943         var stampSize =  stamp.getSize();
39944         var offset = stamp.getBox();
39945         // get the columns that this stamp affects
39946         var firstX = this.isOriginLeft ? offset.x : offset.right;
39947         var lastX = firstX + stampSize.width;
39948         var firstCol = Math.floor( firstX / this.columnWidth );
39949         firstCol = Math.max( 0, firstCol );
39950         
39951         var lastCol = Math.floor( lastX / this.columnWidth );
39952         // lastCol should not go over if multiple of columnWidth #425
39953         lastCol -= lastX % this.columnWidth ? 0 : 1;
39954         lastCol = Math.min( this.cols - 1, lastCol );
39955         
39956         // set colYs to bottom of the stamp
39957         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
39958             stampSize.height;
39959             
39960         for ( var i = firstCol; i <= lastCol; i++ ) {
39961           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
39962         }
39963     },
39964     */
39965     
39966     _getContainerSize : function()
39967     {
39968         this.maxY = Math.max.apply( Math, this.colYs );
39969         var size = {
39970             height: this.maxY
39971         };
39972       
39973         if ( this.isFitWidth ) {
39974             size.width = this._getContainerFitWidth();
39975         }
39976       
39977         return size;
39978     },
39979     
39980     _getContainerFitWidth : function()
39981     {
39982         var unusedCols = 0;
39983         // count unused columns
39984         var i = this.cols;
39985         while ( --i ) {
39986           if ( this.colYs[i] !== 0 ) {
39987             break;
39988           }
39989           unusedCols++;
39990         }
39991         // fit container to columns that have been used
39992         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
39993     },
39994     
39995     needsResizeLayout : function()
39996     {
39997         var previousWidth = this.containerWidth;
39998         this.getContainerWidth();
39999         return previousWidth !== this.containerWidth;
40000     }
40001  
40002 });
40003
40004  
40005
40006  /*
40007  * - LGPL
40008  *
40009  * element
40010  * 
40011  */
40012
40013 /**
40014  * @class Roo.bootstrap.MasonryBrick
40015  * @extends Roo.bootstrap.Component
40016  * Bootstrap MasonryBrick class
40017  * 
40018  * @constructor
40019  * Create a new MasonryBrick
40020  * @param {Object} config The config object
40021  */
40022
40023 Roo.bootstrap.MasonryBrick = function(config){
40024     
40025     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40026     
40027     Roo.bootstrap.MasonryBrick.register(this);
40028     
40029     this.addEvents({
40030         // raw events
40031         /**
40032          * @event click
40033          * When a MasonryBrick is clcik
40034          * @param {Roo.bootstrap.MasonryBrick} this
40035          * @param {Roo.EventObject} e
40036          */
40037         "click" : true
40038     });
40039 };
40040
40041 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40042     
40043     /**
40044      * @cfg {String} title
40045      */   
40046     title : '',
40047     /**
40048      * @cfg {String} html
40049      */   
40050     html : '',
40051     /**
40052      * @cfg {String} bgimage
40053      */   
40054     bgimage : '',
40055     /**
40056      * @cfg {String} videourl
40057      */   
40058     videourl : '',
40059     /**
40060      * @cfg {String} cls
40061      */   
40062     cls : '',
40063     /**
40064      * @cfg {String} href
40065      */   
40066     href : '',
40067     /**
40068      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40069      */   
40070     size : 'xs',
40071     
40072     /**
40073      * @cfg {String} placetitle (center|bottom)
40074      */   
40075     placetitle : '',
40076     
40077     /**
40078      * @cfg {Boolean} isFitContainer defalut true
40079      */   
40080     isFitContainer : true, 
40081     
40082     /**
40083      * @cfg {Boolean} preventDefault defalut false
40084      */   
40085     preventDefault : false, 
40086     
40087     /**
40088      * @cfg {Boolean} inverse defalut false
40089      */   
40090     maskInverse : false, 
40091     
40092     getAutoCreate : function()
40093     {
40094         if(!this.isFitContainer){
40095             return this.getSplitAutoCreate();
40096         }
40097         
40098         var cls = 'masonry-brick masonry-brick-full';
40099         
40100         if(this.href.length){
40101             cls += ' masonry-brick-link';
40102         }
40103         
40104         if(this.bgimage.length){
40105             cls += ' masonry-brick-image';
40106         }
40107         
40108         if(this.maskInverse){
40109             cls += ' mask-inverse';
40110         }
40111         
40112         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40113             cls += ' enable-mask';
40114         }
40115         
40116         if(this.size){
40117             cls += ' masonry-' + this.size + '-brick';
40118         }
40119         
40120         if(this.placetitle.length){
40121             
40122             switch (this.placetitle) {
40123                 case 'center' :
40124                     cls += ' masonry-center-title';
40125                     break;
40126                 case 'bottom' :
40127                     cls += ' masonry-bottom-title';
40128                     break;
40129                 default:
40130                     break;
40131             }
40132             
40133         } else {
40134             if(!this.html.length && !this.bgimage.length){
40135                 cls += ' masonry-center-title';
40136             }
40137
40138             if(!this.html.length && this.bgimage.length){
40139                 cls += ' masonry-bottom-title';
40140             }
40141         }
40142         
40143         if(this.cls){
40144             cls += ' ' + this.cls;
40145         }
40146         
40147         var cfg = {
40148             tag: (this.href.length) ? 'a' : 'div',
40149             cls: cls,
40150             cn: [
40151                 {
40152                     tag: 'div',
40153                     cls: 'masonry-brick-mask'
40154                 },
40155                 {
40156                     tag: 'div',
40157                     cls: 'masonry-brick-paragraph',
40158                     cn: []
40159                 }
40160             ]
40161         };
40162         
40163         if(this.href.length){
40164             cfg.href = this.href;
40165         }
40166         
40167         var cn = cfg.cn[1].cn;
40168         
40169         if(this.title.length){
40170             cn.push({
40171                 tag: 'h4',
40172                 cls: 'masonry-brick-title',
40173                 html: this.title
40174             });
40175         }
40176         
40177         if(this.html.length){
40178             cn.push({
40179                 tag: 'p',
40180                 cls: 'masonry-brick-text',
40181                 html: this.html
40182             });
40183         }
40184         
40185         if (!this.title.length && !this.html.length) {
40186             cfg.cn[1].cls += ' hide';
40187         }
40188         
40189         if(this.bgimage.length){
40190             cfg.cn.push({
40191                 tag: 'img',
40192                 cls: 'masonry-brick-image-view',
40193                 src: this.bgimage
40194             });
40195         }
40196         
40197         if(this.videourl.length){
40198             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40199             // youtube support only?
40200             cfg.cn.push({
40201                 tag: 'iframe',
40202                 cls: 'masonry-brick-image-view',
40203                 src: vurl,
40204                 frameborder : 0,
40205                 allowfullscreen : true
40206             });
40207         }
40208         
40209         return cfg;
40210         
40211     },
40212     
40213     getSplitAutoCreate : function()
40214     {
40215         var cls = 'masonry-brick masonry-brick-split';
40216         
40217         if(this.href.length){
40218             cls += ' masonry-brick-link';
40219         }
40220         
40221         if(this.bgimage.length){
40222             cls += ' masonry-brick-image';
40223         }
40224         
40225         if(this.size){
40226             cls += ' masonry-' + this.size + '-brick';
40227         }
40228         
40229         switch (this.placetitle) {
40230             case 'center' :
40231                 cls += ' masonry-center-title';
40232                 break;
40233             case 'bottom' :
40234                 cls += ' masonry-bottom-title';
40235                 break;
40236             default:
40237                 if(!this.bgimage.length){
40238                     cls += ' masonry-center-title';
40239                 }
40240
40241                 if(this.bgimage.length){
40242                     cls += ' masonry-bottom-title';
40243                 }
40244                 break;
40245         }
40246         
40247         if(this.cls){
40248             cls += ' ' + this.cls;
40249         }
40250         
40251         var cfg = {
40252             tag: (this.href.length) ? 'a' : 'div',
40253             cls: cls,
40254             cn: [
40255                 {
40256                     tag: 'div',
40257                     cls: 'masonry-brick-split-head',
40258                     cn: [
40259                         {
40260                             tag: 'div',
40261                             cls: 'masonry-brick-paragraph',
40262                             cn: []
40263                         }
40264                     ]
40265                 },
40266                 {
40267                     tag: 'div',
40268                     cls: 'masonry-brick-split-body',
40269                     cn: []
40270                 }
40271             ]
40272         };
40273         
40274         if(this.href.length){
40275             cfg.href = this.href;
40276         }
40277         
40278         if(this.title.length){
40279             cfg.cn[0].cn[0].cn.push({
40280                 tag: 'h4',
40281                 cls: 'masonry-brick-title',
40282                 html: this.title
40283             });
40284         }
40285         
40286         if(this.html.length){
40287             cfg.cn[1].cn.push({
40288                 tag: 'p',
40289                 cls: 'masonry-brick-text',
40290                 html: this.html
40291             });
40292         }
40293
40294         if(this.bgimage.length){
40295             cfg.cn[0].cn.push({
40296                 tag: 'img',
40297                 cls: 'masonry-brick-image-view',
40298                 src: this.bgimage
40299             });
40300         }
40301         
40302         if(this.videourl.length){
40303             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40304             // youtube support only?
40305             cfg.cn[0].cn.cn.push({
40306                 tag: 'iframe',
40307                 cls: 'masonry-brick-image-view',
40308                 src: vurl,
40309                 frameborder : 0,
40310                 allowfullscreen : true
40311             });
40312         }
40313         
40314         return cfg;
40315     },
40316     
40317     initEvents: function() 
40318     {
40319         switch (this.size) {
40320             case 'xs' :
40321                 this.x = 1;
40322                 this.y = 1;
40323                 break;
40324             case 'sm' :
40325                 this.x = 2;
40326                 this.y = 2;
40327                 break;
40328             case 'md' :
40329             case 'md-left' :
40330             case 'md-right' :
40331                 this.x = 3;
40332                 this.y = 3;
40333                 break;
40334             case 'tall' :
40335                 this.x = 2;
40336                 this.y = 3;
40337                 break;
40338             case 'wide' :
40339                 this.x = 3;
40340                 this.y = 2;
40341                 break;
40342             case 'wide-thin' :
40343                 this.x = 3;
40344                 this.y = 1;
40345                 break;
40346                         
40347             default :
40348                 break;
40349         }
40350         
40351         if(Roo.isTouch){
40352             this.el.on('touchstart', this.onTouchStart, this);
40353             this.el.on('touchmove', this.onTouchMove, this);
40354             this.el.on('touchend', this.onTouchEnd, this);
40355             this.el.on('contextmenu', this.onContextMenu, this);
40356         } else {
40357             this.el.on('mouseenter'  ,this.enter, this);
40358             this.el.on('mouseleave', this.leave, this);
40359             this.el.on('click', this.onClick, this);
40360         }
40361         
40362         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40363             this.parent().bricks.push(this);   
40364         }
40365         
40366     },
40367     
40368     onClick: function(e, el)
40369     {
40370         var time = this.endTimer - this.startTimer;
40371         // Roo.log(e.preventDefault());
40372         if(Roo.isTouch){
40373             if(time > 1000){
40374                 e.preventDefault();
40375                 return;
40376             }
40377         }
40378         
40379         if(!this.preventDefault){
40380             return;
40381         }
40382         
40383         e.preventDefault();
40384         
40385         if (this.activeClass != '') {
40386             this.selectBrick();
40387         }
40388         
40389         this.fireEvent('click', this, e);
40390     },
40391     
40392     enter: function(e, el)
40393     {
40394         e.preventDefault();
40395         
40396         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40397             return;
40398         }
40399         
40400         if(this.bgimage.length && this.html.length){
40401             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40402         }
40403     },
40404     
40405     leave: function(e, el)
40406     {
40407         e.preventDefault();
40408         
40409         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40410             return;
40411         }
40412         
40413         if(this.bgimage.length && this.html.length){
40414             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40415         }
40416     },
40417     
40418     onTouchStart: function(e, el)
40419     {
40420 //        e.preventDefault();
40421         
40422         this.touchmoved = false;
40423         
40424         if(!this.isFitContainer){
40425             return;
40426         }
40427         
40428         if(!this.bgimage.length || !this.html.length){
40429             return;
40430         }
40431         
40432         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40433         
40434         this.timer = new Date().getTime();
40435         
40436     },
40437     
40438     onTouchMove: function(e, el)
40439     {
40440         this.touchmoved = true;
40441     },
40442     
40443     onContextMenu : function(e,el)
40444     {
40445         e.preventDefault();
40446         e.stopPropagation();
40447         return false;
40448     },
40449     
40450     onTouchEnd: function(e, el)
40451     {
40452 //        e.preventDefault();
40453         
40454         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40455         
40456             this.leave(e,el);
40457             
40458             return;
40459         }
40460         
40461         if(!this.bgimage.length || !this.html.length){
40462             
40463             if(this.href.length){
40464                 window.location.href = this.href;
40465             }
40466             
40467             return;
40468         }
40469         
40470         if(!this.isFitContainer){
40471             return;
40472         }
40473         
40474         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40475         
40476         window.location.href = this.href;
40477     },
40478     
40479     //selection on single brick only
40480     selectBrick : function() {
40481         
40482         if (!this.parentId) {
40483             return;
40484         }
40485         
40486         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40487         var index = m.selectedBrick.indexOf(this.id);
40488         
40489         if ( index > -1) {
40490             m.selectedBrick.splice(index,1);
40491             this.el.removeClass(this.activeClass);
40492             return;
40493         }
40494         
40495         for(var i = 0; i < m.selectedBrick.length; i++) {
40496             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40497             b.el.removeClass(b.activeClass);
40498         }
40499         
40500         m.selectedBrick = [];
40501         
40502         m.selectedBrick.push(this.id);
40503         this.el.addClass(this.activeClass);
40504         return;
40505     },
40506     
40507     isSelected : function(){
40508         return this.el.hasClass(this.activeClass);
40509         
40510     }
40511 });
40512
40513 Roo.apply(Roo.bootstrap.MasonryBrick, {
40514     
40515     //groups: {},
40516     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40517      /**
40518     * register a Masonry Brick
40519     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40520     */
40521     
40522     register : function(brick)
40523     {
40524         //this.groups[brick.id] = brick;
40525         this.groups.add(brick.id, brick);
40526     },
40527     /**
40528     * fetch a  masonry brick based on the masonry brick ID
40529     * @param {string} the masonry brick to add
40530     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
40531     */
40532     
40533     get: function(brick_id) 
40534     {
40535         // if (typeof(this.groups[brick_id]) == 'undefined') {
40536         //     return false;
40537         // }
40538         // return this.groups[brick_id] ;
40539         
40540         if(this.groups.key(brick_id)) {
40541             return this.groups.key(brick_id);
40542         }
40543         
40544         return false;
40545     }
40546     
40547     
40548     
40549 });
40550
40551  /*
40552  * - LGPL
40553  *
40554  * element
40555  * 
40556  */
40557
40558 /**
40559  * @class Roo.bootstrap.Brick
40560  * @extends Roo.bootstrap.Component
40561  * Bootstrap Brick class
40562  * 
40563  * @constructor
40564  * Create a new Brick
40565  * @param {Object} config The config object
40566  */
40567
40568 Roo.bootstrap.Brick = function(config){
40569     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
40570     
40571     this.addEvents({
40572         // raw events
40573         /**
40574          * @event click
40575          * When a Brick is click
40576          * @param {Roo.bootstrap.Brick} this
40577          * @param {Roo.EventObject} e
40578          */
40579         "click" : true
40580     });
40581 };
40582
40583 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
40584     
40585     /**
40586      * @cfg {String} title
40587      */   
40588     title : '',
40589     /**
40590      * @cfg {String} html
40591      */   
40592     html : '',
40593     /**
40594      * @cfg {String} bgimage
40595      */   
40596     bgimage : '',
40597     /**
40598      * @cfg {String} cls
40599      */   
40600     cls : '',
40601     /**
40602      * @cfg {String} href
40603      */   
40604     href : '',
40605     /**
40606      * @cfg {String} video
40607      */   
40608     video : '',
40609     /**
40610      * @cfg {Boolean} square
40611      */   
40612     square : true,
40613     
40614     getAutoCreate : function()
40615     {
40616         var cls = 'roo-brick';
40617         
40618         if(this.href.length){
40619             cls += ' roo-brick-link';
40620         }
40621         
40622         if(this.bgimage.length){
40623             cls += ' roo-brick-image';
40624         }
40625         
40626         if(!this.html.length && !this.bgimage.length){
40627             cls += ' roo-brick-center-title';
40628         }
40629         
40630         if(!this.html.length && this.bgimage.length){
40631             cls += ' roo-brick-bottom-title';
40632         }
40633         
40634         if(this.cls){
40635             cls += ' ' + this.cls;
40636         }
40637         
40638         var cfg = {
40639             tag: (this.href.length) ? 'a' : 'div',
40640             cls: cls,
40641             cn: [
40642                 {
40643                     tag: 'div',
40644                     cls: 'roo-brick-paragraph',
40645                     cn: []
40646                 }
40647             ]
40648         };
40649         
40650         if(this.href.length){
40651             cfg.href = this.href;
40652         }
40653         
40654         var cn = cfg.cn[0].cn;
40655         
40656         if(this.title.length){
40657             cn.push({
40658                 tag: 'h4',
40659                 cls: 'roo-brick-title',
40660                 html: this.title
40661             });
40662         }
40663         
40664         if(this.html.length){
40665             cn.push({
40666                 tag: 'p',
40667                 cls: 'roo-brick-text',
40668                 html: this.html
40669             });
40670         } else {
40671             cn.cls += ' hide';
40672         }
40673         
40674         if(this.bgimage.length){
40675             cfg.cn.push({
40676                 tag: 'img',
40677                 cls: 'roo-brick-image-view',
40678                 src: this.bgimage
40679             });
40680         }
40681         
40682         return cfg;
40683     },
40684     
40685     initEvents: function() 
40686     {
40687         if(this.title.length || this.html.length){
40688             this.el.on('mouseenter'  ,this.enter, this);
40689             this.el.on('mouseleave', this.leave, this);
40690         }
40691         
40692         Roo.EventManager.onWindowResize(this.resize, this); 
40693         
40694         if(this.bgimage.length){
40695             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
40696             this.imageEl.on('load', this.onImageLoad, this);
40697             return;
40698         }
40699         
40700         this.resize();
40701     },
40702     
40703     onImageLoad : function()
40704     {
40705         this.resize();
40706     },
40707     
40708     resize : function()
40709     {
40710         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
40711         
40712         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
40713         
40714         if(this.bgimage.length){
40715             var image = this.el.select('.roo-brick-image-view', true).first();
40716             
40717             image.setWidth(paragraph.getWidth());
40718             
40719             if(this.square){
40720                 image.setHeight(paragraph.getWidth());
40721             }
40722             
40723             this.el.setHeight(image.getHeight());
40724             paragraph.setHeight(image.getHeight());
40725             
40726         }
40727         
40728     },
40729     
40730     enter: function(e, el)
40731     {
40732         e.preventDefault();
40733         
40734         if(this.bgimage.length){
40735             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
40736             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
40737         }
40738     },
40739     
40740     leave: function(e, el)
40741     {
40742         e.preventDefault();
40743         
40744         if(this.bgimage.length){
40745             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
40746             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
40747         }
40748     }
40749     
40750 });
40751
40752  
40753
40754  /*
40755  * - LGPL
40756  *
40757  * Number field 
40758  */
40759
40760 /**
40761  * @class Roo.bootstrap.form.NumberField
40762  * @extends Roo.bootstrap.form.Input
40763  * Bootstrap NumberField class
40764  * 
40765  * 
40766  * 
40767  * 
40768  * @constructor
40769  * Create a new NumberField
40770  * @param {Object} config The config object
40771  */
40772
40773 Roo.bootstrap.form.NumberField = function(config){
40774     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
40775 };
40776
40777 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
40778     
40779     /**
40780      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40781      */
40782     allowDecimals : true,
40783     /**
40784      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40785      */
40786     decimalSeparator : ".",
40787     /**
40788      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40789      */
40790     decimalPrecision : 2,
40791     /**
40792      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40793      */
40794     allowNegative : true,
40795     
40796     /**
40797      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40798      */
40799     allowZero: true,
40800     /**
40801      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40802      */
40803     minValue : Number.NEGATIVE_INFINITY,
40804     /**
40805      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40806      */
40807     maxValue : Number.MAX_VALUE,
40808     /**
40809      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40810      */
40811     minText : "The minimum value for this field is {0}",
40812     /**
40813      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40814      */
40815     maxText : "The maximum value for this field is {0}",
40816     /**
40817      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40818      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40819      */
40820     nanText : "{0} is not a valid number",
40821     /**
40822      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40823      */
40824     thousandsDelimiter : false,
40825     /**
40826      * @cfg {String} valueAlign alignment of value
40827      */
40828     valueAlign : "left",
40829
40830     getAutoCreate : function()
40831     {
40832         var hiddenInput = {
40833             tag: 'input',
40834             type: 'hidden',
40835             id: Roo.id(),
40836             cls: 'hidden-number-input'
40837         };
40838         
40839         if (this.name) {
40840             hiddenInput.name = this.name;
40841         }
40842         
40843         this.name = '';
40844         
40845         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
40846         
40847         this.name = hiddenInput.name;
40848         
40849         if(cfg.cn.length > 0) {
40850             cfg.cn.push(hiddenInput);
40851         }
40852         
40853         return cfg;
40854     },
40855
40856     // private
40857     initEvents : function()
40858     {   
40859         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
40860         
40861         var allowed = "0123456789";
40862         
40863         if(this.allowDecimals){
40864             allowed += this.decimalSeparator;
40865         }
40866         
40867         if(this.allowNegative){
40868             allowed += "-";
40869         }
40870         
40871         if(this.thousandsDelimiter) {
40872             allowed += ",";
40873         }
40874         
40875         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40876         
40877         var keyPress = function(e){
40878             
40879             var k = e.getKey();
40880             
40881             var c = e.getCharCode();
40882             
40883             if(
40884                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40885                     allowed.indexOf(String.fromCharCode(c)) === -1
40886             ){
40887                 e.stopEvent();
40888                 return;
40889             }
40890             
40891             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40892                 return;
40893             }
40894             
40895             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40896                 e.stopEvent();
40897             }
40898         };
40899         
40900         this.el.on("keypress", keyPress, this);
40901     },
40902     
40903     validateValue : function(value)
40904     {
40905         
40906         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
40907             return false;
40908         }
40909         
40910         var num = this.parseValue(value);
40911         
40912         if(isNaN(num)){
40913             this.markInvalid(String.format(this.nanText, value));
40914             return false;
40915         }
40916         
40917         if(num < this.minValue){
40918             this.markInvalid(String.format(this.minText, this.minValue));
40919             return false;
40920         }
40921         
40922         if(num > this.maxValue){
40923             this.markInvalid(String.format(this.maxText, this.maxValue));
40924             return false;
40925         }
40926         
40927         return true;
40928     },
40929
40930     getValue : function()
40931     {
40932         var v = this.hiddenEl().getValue();
40933         
40934         return this.fixPrecision(this.parseValue(v));
40935     },
40936
40937     parseValue : function(value)
40938     {
40939         if(this.thousandsDelimiter) {
40940             value += "";
40941             r = new RegExp(",", "g");
40942             value = value.replace(r, "");
40943         }
40944         
40945         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40946         return isNaN(value) ? '' : value;
40947     },
40948
40949     fixPrecision : function(value)
40950     {
40951         if(this.thousandsDelimiter) {
40952             value += "";
40953             r = new RegExp(",", "g");
40954             value = value.replace(r, "");
40955         }
40956         
40957         var nan = isNaN(value);
40958         
40959         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40960             return nan ? '' : value;
40961         }
40962         return parseFloat(value).toFixed(this.decimalPrecision);
40963     },
40964
40965     setValue : function(v)
40966     {
40967         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40968         
40969         this.value = v;
40970         
40971         if(this.rendered){
40972             
40973             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40974             
40975             this.inputEl().dom.value = (v == '') ? '' :
40976                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40977             
40978             if(!this.allowZero && v === '0') {
40979                 this.hiddenEl().dom.value = '';
40980                 this.inputEl().dom.value = '';
40981             }
40982             
40983             this.validate();
40984         }
40985     },
40986
40987     decimalPrecisionFcn : function(v)
40988     {
40989         return Math.floor(v);
40990     },
40991
40992     beforeBlur : function()
40993     {
40994         var v = this.parseValue(this.getRawValue());
40995         
40996         if(v || v === 0 || v === ''){
40997             this.setValue(v);
40998         }
40999     },
41000     
41001     hiddenEl : function()
41002     {
41003         return this.el.select('input.hidden-number-input',true).first();
41004     }
41005     
41006 });
41007
41008  
41009
41010 /*
41011 * Licence: LGPL
41012 */
41013
41014 /**
41015  * @class Roo.bootstrap.DocumentSlider
41016  * @extends Roo.bootstrap.Component
41017  * Bootstrap DocumentSlider class
41018  * 
41019  * @constructor
41020  * Create a new DocumentViewer
41021  * @param {Object} config The config object
41022  */
41023
41024 Roo.bootstrap.DocumentSlider = function(config){
41025     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41026     
41027     this.files = [];
41028     
41029     this.addEvents({
41030         /**
41031          * @event initial
41032          * Fire after initEvent
41033          * @param {Roo.bootstrap.DocumentSlider} this
41034          */
41035         "initial" : true,
41036         /**
41037          * @event update
41038          * Fire after update
41039          * @param {Roo.bootstrap.DocumentSlider} this
41040          */
41041         "update" : true,
41042         /**
41043          * @event click
41044          * Fire after click
41045          * @param {Roo.bootstrap.DocumentSlider} this
41046          */
41047         "click" : true
41048     });
41049 };
41050
41051 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41052     
41053     files : false,
41054     
41055     indicator : 0,
41056     
41057     getAutoCreate : function()
41058     {
41059         var cfg = {
41060             tag : 'div',
41061             cls : 'roo-document-slider',
41062             cn : [
41063                 {
41064                     tag : 'div',
41065                     cls : 'roo-document-slider-header',
41066                     cn : [
41067                         {
41068                             tag : 'div',
41069                             cls : 'roo-document-slider-header-title'
41070                         }
41071                     ]
41072                 },
41073                 {
41074                     tag : 'div',
41075                     cls : 'roo-document-slider-body',
41076                     cn : [
41077                         {
41078                             tag : 'div',
41079                             cls : 'roo-document-slider-prev',
41080                             cn : [
41081                                 {
41082                                     tag : 'i',
41083                                     cls : 'fa fa-chevron-left'
41084                                 }
41085                             ]
41086                         },
41087                         {
41088                             tag : 'div',
41089                             cls : 'roo-document-slider-thumb',
41090                             cn : [
41091                                 {
41092                                     tag : 'img',
41093                                     cls : 'roo-document-slider-image'
41094                                 }
41095                             ]
41096                         },
41097                         {
41098                             tag : 'div',
41099                             cls : 'roo-document-slider-next',
41100                             cn : [
41101                                 {
41102                                     tag : 'i',
41103                                     cls : 'fa fa-chevron-right'
41104                                 }
41105                             ]
41106                         }
41107                     ]
41108                 }
41109             ]
41110         };
41111         
41112         return cfg;
41113     },
41114     
41115     initEvents : function()
41116     {
41117         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41118         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41119         
41120         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41121         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41122         
41123         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41124         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41125         
41126         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41127         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41128         
41129         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41130         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41131         
41132         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41133         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41134         
41135         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41136         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41137         
41138         this.thumbEl.on('click', this.onClick, this);
41139         
41140         this.prevIndicator.on('click', this.prev, this);
41141         
41142         this.nextIndicator.on('click', this.next, this);
41143         
41144     },
41145     
41146     initial : function()
41147     {
41148         if(this.files.length){
41149             this.indicator = 1;
41150             this.update()
41151         }
41152         
41153         this.fireEvent('initial', this);
41154     },
41155     
41156     update : function()
41157     {
41158         this.imageEl.attr('src', this.files[this.indicator - 1]);
41159         
41160         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41161         
41162         this.prevIndicator.show();
41163         
41164         if(this.indicator == 1){
41165             this.prevIndicator.hide();
41166         }
41167         
41168         this.nextIndicator.show();
41169         
41170         if(this.indicator == this.files.length){
41171             this.nextIndicator.hide();
41172         }
41173         
41174         this.thumbEl.scrollTo('top');
41175         
41176         this.fireEvent('update', this);
41177     },
41178     
41179     onClick : function(e)
41180     {
41181         e.preventDefault();
41182         
41183         this.fireEvent('click', this);
41184     },
41185     
41186     prev : function(e)
41187     {
41188         e.preventDefault();
41189         
41190         this.indicator = Math.max(1, this.indicator - 1);
41191         
41192         this.update();
41193     },
41194     
41195     next : function(e)
41196     {
41197         e.preventDefault();
41198         
41199         this.indicator = Math.min(this.files.length, this.indicator + 1);
41200         
41201         this.update();
41202     }
41203 });
41204 /*
41205  * - LGPL
41206  *
41207  * RadioSet
41208  *
41209  *
41210  */
41211
41212 /**
41213  * @class Roo.bootstrap.form.RadioSet
41214  * @extends Roo.bootstrap.form.Input
41215  * @children Roo.bootstrap.form.Radio
41216  * Bootstrap RadioSet class
41217  * @cfg {String} indicatorpos (left|right) default left
41218  * @cfg {Boolean} inline (true|false) inline the element (default true)
41219  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41220  * @constructor
41221  * Create a new RadioSet
41222  * @param {Object} config The config object
41223  */
41224
41225 Roo.bootstrap.form.RadioSet = function(config){
41226     
41227     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41228     
41229     this.radioes = [];
41230     
41231     Roo.bootstrap.form.RadioSet.register(this);
41232     
41233     this.addEvents({
41234         /**
41235         * @event check
41236         * Fires when the element is checked or unchecked.
41237         * @param {Roo.bootstrap.form.RadioSet} this This radio
41238         * @param {Roo.bootstrap.form.Radio} item The checked item
41239         */
41240        check : true,
41241        /**
41242         * @event click
41243         * Fires when the element is click.
41244         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41245         * @param {Roo.bootstrap.form.Radio} item The checked item
41246         * @param {Roo.EventObject} e The event object
41247         */
41248        click : true
41249     });
41250     
41251 };
41252
41253 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41254
41255     radioes : false,
41256     
41257     inline : true,
41258     
41259     weight : '',
41260     
41261     indicatorpos : 'left',
41262     
41263     getAutoCreate : function()
41264     {
41265         var label = {
41266             tag : 'label',
41267             cls : 'roo-radio-set-label',
41268             cn : [
41269                 {
41270                     tag : 'span',
41271                     html : this.fieldLabel
41272                 }
41273             ]
41274         };
41275         if (Roo.bootstrap.version == 3) {
41276             
41277             
41278             if(this.indicatorpos == 'left'){
41279                 label.cn.unshift({
41280                     tag : 'i',
41281                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41282                     tooltip : 'This field is required'
41283                 });
41284             } else {
41285                 label.cn.push({
41286                     tag : 'i',
41287                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41288                     tooltip : 'This field is required'
41289                 });
41290             }
41291         }
41292         var items = {
41293             tag : 'div',
41294             cls : 'roo-radio-set-items'
41295         };
41296         
41297         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41298         
41299         if (align === 'left' && this.fieldLabel.length) {
41300             
41301             items = {
41302                 cls : "roo-radio-set-right", 
41303                 cn: [
41304                     items
41305                 ]
41306             };
41307             
41308             if(this.labelWidth > 12){
41309                 label.style = "width: " + this.labelWidth + 'px';
41310             }
41311             
41312             if(this.labelWidth < 13 && this.labelmd == 0){
41313                 this.labelmd = this.labelWidth;
41314             }
41315             
41316             if(this.labellg > 0){
41317                 label.cls += ' col-lg-' + this.labellg;
41318                 items.cls += ' col-lg-' + (12 - this.labellg);
41319             }
41320             
41321             if(this.labelmd > 0){
41322                 label.cls += ' col-md-' + this.labelmd;
41323                 items.cls += ' col-md-' + (12 - this.labelmd);
41324             }
41325             
41326             if(this.labelsm > 0){
41327                 label.cls += ' col-sm-' + this.labelsm;
41328                 items.cls += ' col-sm-' + (12 - this.labelsm);
41329             }
41330             
41331             if(this.labelxs > 0){
41332                 label.cls += ' col-xs-' + this.labelxs;
41333                 items.cls += ' col-xs-' + (12 - this.labelxs);
41334             }
41335         }
41336         
41337         var cfg = {
41338             tag : 'div',
41339             cls : 'roo-radio-set',
41340             cn : [
41341                 {
41342                     tag : 'input',
41343                     cls : 'roo-radio-set-input',
41344                     type : 'hidden',
41345                     name : this.name,
41346                     value : this.value ? this.value :  ''
41347                 },
41348                 label,
41349                 items
41350             ]
41351         };
41352         
41353         if(this.weight.length){
41354             cfg.cls += ' roo-radio-' + this.weight;
41355         }
41356         
41357         if(this.inline) {
41358             cfg.cls += ' roo-radio-set-inline';
41359         }
41360         
41361         var settings=this;
41362         ['xs','sm','md','lg'].map(function(size){
41363             if (settings[size]) {
41364                 cfg.cls += ' col-' + size + '-' + settings[size];
41365             }
41366         });
41367         
41368         return cfg;
41369         
41370     },
41371
41372     initEvents : function()
41373     {
41374         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41375         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41376         
41377         if(!this.fieldLabel.length){
41378             this.labelEl.hide();
41379         }
41380         
41381         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41382         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41383         
41384         this.indicator = this.indicatorEl();
41385         
41386         if(this.indicator){
41387             this.indicator.addClass('invisible');
41388         }
41389         
41390         this.originalValue = this.getValue();
41391         
41392     },
41393     
41394     inputEl: function ()
41395     {
41396         return this.el.select('.roo-radio-set-input', true).first();
41397     },
41398     
41399     getChildContainer : function()
41400     {
41401         return this.itemsEl;
41402     },
41403     
41404     register : function(item)
41405     {
41406         this.radioes.push(item);
41407         
41408     },
41409     
41410     validate : function()
41411     {   
41412         if(this.getVisibilityEl().hasClass('hidden')){
41413             return true;
41414         }
41415         
41416         var valid = false;
41417         
41418         Roo.each(this.radioes, function(i){
41419             if(!i.checked){
41420                 return;
41421             }
41422             
41423             valid = true;
41424             return false;
41425         });
41426         
41427         if(this.allowBlank) {
41428             return true;
41429         }
41430         
41431         if(this.disabled || valid){
41432             this.markValid();
41433             return true;
41434         }
41435         
41436         this.markInvalid();
41437         return false;
41438         
41439     },
41440     
41441     markValid : function()
41442     {
41443         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41444             this.indicatorEl().removeClass('visible');
41445             this.indicatorEl().addClass('invisible');
41446         }
41447         
41448         
41449         if (Roo.bootstrap.version == 3) {
41450             this.el.removeClass([this.invalidClass, this.validClass]);
41451             this.el.addClass(this.validClass);
41452         } else {
41453             this.el.removeClass(['is-invalid','is-valid']);
41454             this.el.addClass(['is-valid']);
41455         }
41456         this.fireEvent('valid', this);
41457     },
41458     
41459     markInvalid : function(msg)
41460     {
41461         if(this.allowBlank || this.disabled){
41462             return;
41463         }
41464         
41465         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41466             this.indicatorEl().removeClass('invisible');
41467             this.indicatorEl().addClass('visible');
41468         }
41469         if (Roo.bootstrap.version == 3) {
41470             this.el.removeClass([this.invalidClass, this.validClass]);
41471             this.el.addClass(this.invalidClass);
41472         } else {
41473             this.el.removeClass(['is-invalid','is-valid']);
41474             this.el.addClass(['is-invalid']);
41475         }
41476         
41477         this.fireEvent('invalid', this, msg);
41478         
41479     },
41480     
41481     setValue : function(v, suppressEvent)
41482     {   
41483         if(this.value === v){
41484             return;
41485         }
41486         
41487         this.value = v;
41488         
41489         if(this.rendered){
41490             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41491         }
41492         
41493         Roo.each(this.radioes, function(i){
41494             i.checked = false;
41495             i.el.removeClass('checked');
41496         });
41497         
41498         Roo.each(this.radioes, function(i){
41499             
41500             if(i.value === v || i.value.toString() === v.toString()){
41501                 i.checked = true;
41502                 i.el.addClass('checked');
41503                 
41504                 if(suppressEvent !== true){
41505                     this.fireEvent('check', this, i);
41506                 }
41507                 
41508                 return false;
41509             }
41510             
41511         }, this);
41512         
41513         this.validate();
41514     },
41515     
41516     clearInvalid : function(){
41517         
41518         if(!this.el || this.preventMark){
41519             return;
41520         }
41521         
41522         this.el.removeClass([this.invalidClass]);
41523         
41524         this.fireEvent('valid', this);
41525     }
41526     
41527 });
41528
41529 Roo.apply(Roo.bootstrap.form.RadioSet, {
41530     
41531     groups: {},
41532     
41533     register : function(set)
41534     {
41535         this.groups[set.name] = set;
41536     },
41537     
41538     get: function(name) 
41539     {
41540         if (typeof(this.groups[name]) == 'undefined') {
41541             return false;
41542         }
41543         
41544         return this.groups[name] ;
41545     }
41546     
41547 });
41548 /*
41549  * Based on:
41550  * Ext JS Library 1.1.1
41551  * Copyright(c) 2006-2007, Ext JS, LLC.
41552  *
41553  * Originally Released Under LGPL - original licence link has changed is not relivant.
41554  *
41555  * Fork - LGPL
41556  * <script type="text/javascript">
41557  */
41558
41559
41560 /**
41561  * @class Roo.bootstrap.SplitBar
41562  * @extends Roo.util.Observable
41563  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
41564  * <br><br>
41565  * Usage:
41566  * <pre><code>
41567 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
41568                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
41569 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
41570 split.minSize = 100;
41571 split.maxSize = 600;
41572 split.animate = true;
41573 split.on('moved', splitterMoved);
41574 </code></pre>
41575  * @constructor
41576  * Create a new SplitBar
41577  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
41578  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
41579  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41580  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
41581                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
41582                         position of the SplitBar).
41583  */
41584 Roo.bootstrap.SplitBar = function(cfg){
41585     
41586     /** @private */
41587     
41588     //{
41589     //  dragElement : elm
41590     //  resizingElement: el,
41591         // optional..
41592     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
41593     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
41594         // existingProxy ???
41595     //}
41596     
41597     this.el = Roo.get(cfg.dragElement, true);
41598     this.el.dom.unselectable = "on";
41599     /** @private */
41600     this.resizingEl = Roo.get(cfg.resizingElement, true);
41601
41602     /**
41603      * @private
41604      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41605      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
41606      * @type Number
41607      */
41608     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
41609     
41610     /**
41611      * The minimum size of the resizing element. (Defaults to 0)
41612      * @type Number
41613      */
41614     this.minSize = 0;
41615     
41616     /**
41617      * The maximum size of the resizing element. (Defaults to 2000)
41618      * @type Number
41619      */
41620     this.maxSize = 2000;
41621     
41622     /**
41623      * Whether to animate the transition to the new size
41624      * @type Boolean
41625      */
41626     this.animate = false;
41627     
41628     /**
41629      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
41630      * @type Boolean
41631      */
41632     this.useShim = false;
41633     
41634     /** @private */
41635     this.shim = null;
41636     
41637     if(!cfg.existingProxy){
41638         /** @private */
41639         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
41640     }else{
41641         this.proxy = Roo.get(cfg.existingProxy).dom;
41642     }
41643     /** @private */
41644     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
41645     
41646     /** @private */
41647     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
41648     
41649     /** @private */
41650     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
41651     
41652     /** @private */
41653     this.dragSpecs = {};
41654     
41655     /**
41656      * @private The adapter to use to positon and resize elements
41657      */
41658     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41659     this.adapter.init(this);
41660     
41661     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41662         /** @private */
41663         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
41664         this.el.addClass("roo-splitbar-h");
41665     }else{
41666         /** @private */
41667         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
41668         this.el.addClass("roo-splitbar-v");
41669     }
41670     
41671     this.addEvents({
41672         /**
41673          * @event resize
41674          * Fires when the splitter is moved (alias for {@link #event-moved})
41675          * @param {Roo.bootstrap.SplitBar} this
41676          * @param {Number} newSize the new width or height
41677          */
41678         "resize" : true,
41679         /**
41680          * @event moved
41681          * Fires when the splitter is moved
41682          * @param {Roo.bootstrap.SplitBar} this
41683          * @param {Number} newSize the new width or height
41684          */
41685         "moved" : true,
41686         /**
41687          * @event beforeresize
41688          * Fires before the splitter is dragged
41689          * @param {Roo.bootstrap.SplitBar} this
41690          */
41691         "beforeresize" : true,
41692
41693         "beforeapply" : true
41694     });
41695
41696     Roo.util.Observable.call(this);
41697 };
41698
41699 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
41700     onStartProxyDrag : function(x, y){
41701         this.fireEvent("beforeresize", this);
41702         if(!this.overlay){
41703             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
41704             o.unselectable();
41705             o.enableDisplayMode("block");
41706             // all splitbars share the same overlay
41707             Roo.bootstrap.SplitBar.prototype.overlay = o;
41708         }
41709         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
41710         this.overlay.show();
41711         Roo.get(this.proxy).setDisplayed("block");
41712         var size = this.adapter.getElementSize(this);
41713         this.activeMinSize = this.getMinimumSize();;
41714         this.activeMaxSize = this.getMaximumSize();;
41715         var c1 = size - this.activeMinSize;
41716         var c2 = Math.max(this.activeMaxSize - size, 0);
41717         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41718             this.dd.resetConstraints();
41719             this.dd.setXConstraint(
41720                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
41721                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
41722             );
41723             this.dd.setYConstraint(0, 0);
41724         }else{
41725             this.dd.resetConstraints();
41726             this.dd.setXConstraint(0, 0);
41727             this.dd.setYConstraint(
41728                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
41729                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
41730             );
41731          }
41732         this.dragSpecs.startSize = size;
41733         this.dragSpecs.startPoint = [x, y];
41734         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
41735     },
41736     
41737     /** 
41738      * @private Called after the drag operation by the DDProxy
41739      */
41740     onEndProxyDrag : function(e){
41741         Roo.get(this.proxy).setDisplayed(false);
41742         var endPoint = Roo.lib.Event.getXY(e);
41743         if(this.overlay){
41744             this.overlay.hide();
41745         }
41746         var newSize;
41747         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41748             newSize = this.dragSpecs.startSize + 
41749                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
41750                     endPoint[0] - this.dragSpecs.startPoint[0] :
41751                     this.dragSpecs.startPoint[0] - endPoint[0]
41752                 );
41753         }else{
41754             newSize = this.dragSpecs.startSize + 
41755                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
41756                     endPoint[1] - this.dragSpecs.startPoint[1] :
41757                     this.dragSpecs.startPoint[1] - endPoint[1]
41758                 );
41759         }
41760         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
41761         if(newSize != this.dragSpecs.startSize){
41762             if(this.fireEvent('beforeapply', this, newSize) !== false){
41763                 this.adapter.setElementSize(this, newSize);
41764                 this.fireEvent("moved", this, newSize);
41765                 this.fireEvent("resize", this, newSize);
41766             }
41767         }
41768     },
41769     
41770     /**
41771      * Get the adapter this SplitBar uses
41772      * @return The adapter object
41773      */
41774     getAdapter : function(){
41775         return this.adapter;
41776     },
41777     
41778     /**
41779      * Set the adapter this SplitBar uses
41780      * @param {Object} adapter A SplitBar adapter object
41781      */
41782     setAdapter : function(adapter){
41783         this.adapter = adapter;
41784         this.adapter.init(this);
41785     },
41786     
41787     /**
41788      * Gets the minimum size for the resizing element
41789      * @return {Number} The minimum size
41790      */
41791     getMinimumSize : function(){
41792         return this.minSize;
41793     },
41794     
41795     /**
41796      * Sets the minimum size for the resizing element
41797      * @param {Number} minSize The minimum size
41798      */
41799     setMinimumSize : function(minSize){
41800         this.minSize = minSize;
41801     },
41802     
41803     /**
41804      * Gets the maximum size for the resizing element
41805      * @return {Number} The maximum size
41806      */
41807     getMaximumSize : function(){
41808         return this.maxSize;
41809     },
41810     
41811     /**
41812      * Sets the maximum size for the resizing element
41813      * @param {Number} maxSize The maximum size
41814      */
41815     setMaximumSize : function(maxSize){
41816         this.maxSize = maxSize;
41817     },
41818     
41819     /**
41820      * Sets the initialize size for the resizing element
41821      * @param {Number} size The initial size
41822      */
41823     setCurrentSize : function(size){
41824         var oldAnimate = this.animate;
41825         this.animate = false;
41826         this.adapter.setElementSize(this, size);
41827         this.animate = oldAnimate;
41828     },
41829     
41830     /**
41831      * Destroy this splitbar. 
41832      * @param {Boolean} removeEl True to remove the element
41833      */
41834     destroy : function(removeEl){
41835         if(this.shim){
41836             this.shim.remove();
41837         }
41838         this.dd.unreg();
41839         this.proxy.parentNode.removeChild(this.proxy);
41840         if(removeEl){
41841             this.el.remove();
41842         }
41843     }
41844 });
41845
41846 /**
41847  * @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.
41848  */
41849 Roo.bootstrap.SplitBar.createProxy = function(dir){
41850     var proxy = new Roo.Element(document.createElement("div"));
41851     proxy.unselectable();
41852     var cls = 'roo-splitbar-proxy';
41853     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
41854     document.body.appendChild(proxy.dom);
41855     return proxy.dom;
41856 };
41857
41858 /** 
41859  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
41860  * Default Adapter. It assumes the splitter and resizing element are not positioned
41861  * elements and only gets/sets the width of the element. Generally used for table based layouts.
41862  */
41863 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
41864 };
41865
41866 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
41867     // do nothing for now
41868     init : function(s){
41869     
41870     },
41871     /**
41872      * Called before drag operations to get the current size of the resizing element. 
41873      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41874      */
41875      getElementSize : function(s){
41876         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41877             return s.resizingEl.getWidth();
41878         }else{
41879             return s.resizingEl.getHeight();
41880         }
41881     },
41882     
41883     /**
41884      * Called after drag operations to set the size of the resizing element.
41885      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41886      * @param {Number} newSize The new size to set
41887      * @param {Function} onComplete A function to be invoked when resizing is complete
41888      */
41889     setElementSize : function(s, newSize, onComplete){
41890         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41891             if(!s.animate){
41892                 s.resizingEl.setWidth(newSize);
41893                 if(onComplete){
41894                     onComplete(s, newSize);
41895                 }
41896             }else{
41897                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
41898             }
41899         }else{
41900             
41901             if(!s.animate){
41902                 s.resizingEl.setHeight(newSize);
41903                 if(onComplete){
41904                     onComplete(s, newSize);
41905                 }
41906             }else{
41907                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
41908             }
41909         }
41910     }
41911 };
41912
41913 /** 
41914  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
41915  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
41916  * Adapter that  moves the splitter element to align with the resized sizing element. 
41917  * Used with an absolute positioned SplitBar.
41918  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
41919  * document.body, make sure you assign an id to the body element.
41920  */
41921 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
41922     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41923     this.container = Roo.get(container);
41924 };
41925
41926 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
41927     init : function(s){
41928         this.basic.init(s);
41929     },
41930     
41931     getElementSize : function(s){
41932         return this.basic.getElementSize(s);
41933     },
41934     
41935     setElementSize : function(s, newSize, onComplete){
41936         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
41937     },
41938     
41939     moveSplitter : function(s){
41940         var yes = Roo.bootstrap.SplitBar;
41941         switch(s.placement){
41942             case yes.LEFT:
41943                 s.el.setX(s.resizingEl.getRight());
41944                 break;
41945             case yes.RIGHT:
41946                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
41947                 break;
41948             case yes.TOP:
41949                 s.el.setY(s.resizingEl.getBottom());
41950                 break;
41951             case yes.BOTTOM:
41952                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
41953                 break;
41954         }
41955     }
41956 };
41957
41958 /**
41959  * Orientation constant - Create a vertical SplitBar
41960  * @static
41961  * @type Number
41962  */
41963 Roo.bootstrap.SplitBar.VERTICAL = 1;
41964
41965 /**
41966  * Orientation constant - Create a horizontal SplitBar
41967  * @static
41968  * @type Number
41969  */
41970 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
41971
41972 /**
41973  * Placement constant - The resizing element is to the left of the splitter element
41974  * @static
41975  * @type Number
41976  */
41977 Roo.bootstrap.SplitBar.LEFT = 1;
41978
41979 /**
41980  * Placement constant - The resizing element is to the right of the splitter element
41981  * @static
41982  * @type Number
41983  */
41984 Roo.bootstrap.SplitBar.RIGHT = 2;
41985
41986 /**
41987  * Placement constant - The resizing element is positioned above the splitter element
41988  * @static
41989  * @type Number
41990  */
41991 Roo.bootstrap.SplitBar.TOP = 3;
41992
41993 /**
41994  * Placement constant - The resizing element is positioned under splitter element
41995  * @static
41996  * @type Number
41997  */
41998 Roo.bootstrap.SplitBar.BOTTOM = 4;
41999 /*
42000  * Based on:
42001  * Ext JS Library 1.1.1
42002  * Copyright(c) 2006-2007, Ext JS, LLC.
42003  *
42004  * Originally Released Under LGPL - original licence link has changed is not relivant.
42005  *
42006  * Fork - LGPL
42007  * <script type="text/javascript">
42008  */
42009
42010 /**
42011  * @class Roo.bootstrap.layout.Manager
42012  * @extends Roo.bootstrap.Component
42013  * @abstract
42014  * Base class for layout managers.
42015  */
42016 Roo.bootstrap.layout.Manager = function(config)
42017 {
42018     this.monitorWindowResize = true; // do this before we apply configuration.
42019     
42020     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42021
42022
42023
42024
42025
42026     /** false to disable window resize monitoring @type Boolean */
42027     
42028     this.regions = {};
42029     this.addEvents({
42030         /**
42031          * @event layout
42032          * Fires when a layout is performed.
42033          * @param {Roo.LayoutManager} this
42034          */
42035         "layout" : true,
42036         /**
42037          * @event regionresized
42038          * Fires when the user resizes a region.
42039          * @param {Roo.LayoutRegion} region The resized region
42040          * @param {Number} newSize The new size (width for east/west, height for north/south)
42041          */
42042         "regionresized" : true,
42043         /**
42044          * @event regioncollapsed
42045          * Fires when a region is collapsed.
42046          * @param {Roo.LayoutRegion} region The collapsed region
42047          */
42048         "regioncollapsed" : true,
42049         /**
42050          * @event regionexpanded
42051          * Fires when a region is expanded.
42052          * @param {Roo.LayoutRegion} region The expanded region
42053          */
42054         "regionexpanded" : true
42055     });
42056     this.updating = false;
42057
42058     if (config.el) {
42059         this.el = Roo.get(config.el);
42060         this.initEvents();
42061     }
42062
42063 };
42064
42065 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42066
42067
42068     regions : null,
42069
42070     monitorWindowResize : true,
42071
42072
42073     updating : false,
42074
42075
42076     onRender : function(ct, position)
42077     {
42078         if(!this.el){
42079             this.el = Roo.get(ct);
42080             this.initEvents();
42081         }
42082         //this.fireEvent('render',this);
42083     },
42084
42085
42086     initEvents: function()
42087     {
42088
42089
42090         // ie scrollbar fix
42091         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42092             document.body.scroll = "no";
42093         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42094             this.el.position('relative');
42095         }
42096         this.id = this.el.id;
42097         this.el.addClass("roo-layout-container");
42098         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42099         if(this.el.dom != document.body ) {
42100             this.el.on('resize', this.layout,this);
42101             this.el.on('show', this.layout,this);
42102         }
42103
42104     },
42105
42106     /**
42107      * Returns true if this layout is currently being updated
42108      * @return {Boolean}
42109      */
42110     isUpdating : function(){
42111         return this.updating;
42112     },
42113
42114     /**
42115      * Suspend the LayoutManager from doing auto-layouts while
42116      * making multiple add or remove calls
42117      */
42118     beginUpdate : function(){
42119         this.updating = true;
42120     },
42121
42122     /**
42123      * Restore auto-layouts and optionally disable the manager from performing a layout
42124      * @param {Boolean} noLayout true to disable a layout update
42125      */
42126     endUpdate : function(noLayout){
42127         this.updating = false;
42128         if(!noLayout){
42129             this.layout();
42130         }
42131     },
42132
42133     layout: function(){
42134         // abstract...
42135     },
42136
42137     onRegionResized : function(region, newSize){
42138         this.fireEvent("regionresized", region, newSize);
42139         this.layout();
42140     },
42141
42142     onRegionCollapsed : function(region){
42143         this.fireEvent("regioncollapsed", region);
42144     },
42145
42146     onRegionExpanded : function(region){
42147         this.fireEvent("regionexpanded", region);
42148     },
42149
42150     /**
42151      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42152      * performs box-model adjustments.
42153      * @return {Object} The size as an object {width: (the width), height: (the height)}
42154      */
42155     getViewSize : function()
42156     {
42157         var size;
42158         if(this.el.dom != document.body){
42159             size = this.el.getSize();
42160         }else{
42161             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42162         }
42163         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42164         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42165         return size;
42166     },
42167
42168     /**
42169      * Returns the Element this layout is bound to.
42170      * @return {Roo.Element}
42171      */
42172     getEl : function(){
42173         return this.el;
42174     },
42175
42176     /**
42177      * Returns the specified region.
42178      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42179      * @return {Roo.LayoutRegion}
42180      */
42181     getRegion : function(target){
42182         return this.regions[target.toLowerCase()];
42183     },
42184
42185     onWindowResize : function(){
42186         if(this.monitorWindowResize){
42187             this.layout();
42188         }
42189     }
42190 });
42191 /*
42192  * Based on:
42193  * Ext JS Library 1.1.1
42194  * Copyright(c) 2006-2007, Ext JS, LLC.
42195  *
42196  * Originally Released Under LGPL - original licence link has changed is not relivant.
42197  *
42198  * Fork - LGPL
42199  * <script type="text/javascript">
42200  */
42201 /**
42202  * @class Roo.bootstrap.layout.Border
42203  * @extends Roo.bootstrap.layout.Manager
42204  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42205  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42206  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42207  * please see: examples/bootstrap/nested.html<br><br>
42208  
42209 <b>The container the layout is rendered into can be either the body element or any other element.
42210 If it is not the body element, the container needs to either be an absolute positioned element,
42211 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42212 the container size if it is not the body element.</b>
42213
42214 * @constructor
42215 * Create a new Border
42216 * @param {Object} config Configuration options
42217  */
42218 Roo.bootstrap.layout.Border = function(config){
42219     config = config || {};
42220     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42221     
42222     
42223     
42224     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42225         if(config[region]){
42226             config[region].region = region;
42227             this.addRegion(config[region]);
42228         }
42229     },this);
42230     
42231 };
42232
42233 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42234
42235 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42236     
42237         /**
42238          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42239          */
42240         /**
42241          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42242          */
42243         /**
42244          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42245          */
42246         /**
42247          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42248          */
42249         /**
42250          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42251          */
42252         
42253         
42254         
42255         
42256     parent : false, // this might point to a 'nest' or a ???
42257     
42258     /**
42259      * Creates and adds a new region if it doesn't already exist.
42260      * @param {String} target The target region key (north, south, east, west or center).
42261      * @param {Object} config The regions config object
42262      * @return {BorderLayoutRegion} The new region
42263      */
42264     addRegion : function(config)
42265     {
42266         if(!this.regions[config.region]){
42267             var r = this.factory(config);
42268             this.bindRegion(r);
42269         }
42270         return this.regions[config.region];
42271     },
42272
42273     // private (kinda)
42274     bindRegion : function(r){
42275         this.regions[r.config.region] = r;
42276         
42277         r.on("visibilitychange",    this.layout, this);
42278         r.on("paneladded",          this.layout, this);
42279         r.on("panelremoved",        this.layout, this);
42280         r.on("invalidated",         this.layout, this);
42281         r.on("resized",             this.onRegionResized, this);
42282         r.on("collapsed",           this.onRegionCollapsed, this);
42283         r.on("expanded",            this.onRegionExpanded, this);
42284     },
42285
42286     /**
42287      * Performs a layout update.
42288      */
42289     layout : function()
42290     {
42291         if(this.updating) {
42292             return;
42293         }
42294         
42295         // render all the rebions if they have not been done alreayd?
42296         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42297             if(this.regions[region] && !this.regions[region].bodyEl){
42298                 this.regions[region].onRender(this.el)
42299             }
42300         },this);
42301         
42302         var size = this.getViewSize();
42303         var w = size.width;
42304         var h = size.height;
42305         var centerW = w;
42306         var centerH = h;
42307         var centerY = 0;
42308         var centerX = 0;
42309         //var x = 0, y = 0;
42310
42311         var rs = this.regions;
42312         var north = rs["north"];
42313         var south = rs["south"]; 
42314         var west = rs["west"];
42315         var east = rs["east"];
42316         var center = rs["center"];
42317         //if(this.hideOnLayout){ // not supported anymore
42318             //c.el.setStyle("display", "none");
42319         //}
42320         if(north && north.isVisible()){
42321             var b = north.getBox();
42322             var m = north.getMargins();
42323             b.width = w - (m.left+m.right);
42324             b.x = m.left;
42325             b.y = m.top;
42326             centerY = b.height + b.y + m.bottom;
42327             centerH -= centerY;
42328             north.updateBox(this.safeBox(b));
42329         }
42330         if(south && south.isVisible()){
42331             var b = south.getBox();
42332             var m = south.getMargins();
42333             b.width = w - (m.left+m.right);
42334             b.x = m.left;
42335             var totalHeight = (b.height + m.top + m.bottom);
42336             b.y = h - totalHeight + m.top;
42337             centerH -= totalHeight;
42338             south.updateBox(this.safeBox(b));
42339         }
42340         if(west && west.isVisible()){
42341             var b = west.getBox();
42342             var m = west.getMargins();
42343             b.height = centerH - (m.top+m.bottom);
42344             b.x = m.left;
42345             b.y = centerY + m.top;
42346             var totalWidth = (b.width + m.left + m.right);
42347             centerX += totalWidth;
42348             centerW -= totalWidth;
42349             west.updateBox(this.safeBox(b));
42350         }
42351         if(east && east.isVisible()){
42352             var b = east.getBox();
42353             var m = east.getMargins();
42354             b.height = centerH - (m.top+m.bottom);
42355             var totalWidth = (b.width + m.left + m.right);
42356             b.x = w - totalWidth + m.left;
42357             b.y = centerY + m.top;
42358             centerW -= totalWidth;
42359             east.updateBox(this.safeBox(b));
42360         }
42361         if(center){
42362             var m = center.getMargins();
42363             var centerBox = {
42364                 x: centerX + m.left,
42365                 y: centerY + m.top,
42366                 width: centerW - (m.left+m.right),
42367                 height: centerH - (m.top+m.bottom)
42368             };
42369             //if(this.hideOnLayout){
42370                 //center.el.setStyle("display", "block");
42371             //}
42372             center.updateBox(this.safeBox(centerBox));
42373         }
42374         this.el.repaint();
42375         this.fireEvent("layout", this);
42376     },
42377
42378     // private
42379     safeBox : function(box){
42380         box.width = Math.max(0, box.width);
42381         box.height = Math.max(0, box.height);
42382         return box;
42383     },
42384
42385     /**
42386      * Adds a ContentPanel (or subclass) to this layout.
42387      * @param {String} target The target region key (north, south, east, west or center).
42388      * @param {Roo.ContentPanel} panel The panel to add
42389      * @return {Roo.ContentPanel} The added panel
42390      */
42391     add : function(target, panel){
42392          
42393         target = target.toLowerCase();
42394         return this.regions[target].add(panel);
42395     },
42396
42397     /**
42398      * Remove a ContentPanel (or subclass) to this layout.
42399      * @param {String} target The target region key (north, south, east, west or center).
42400      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42401      * @return {Roo.ContentPanel} The removed panel
42402      */
42403     remove : function(target, panel){
42404         target = target.toLowerCase();
42405         return this.regions[target].remove(panel);
42406     },
42407
42408     /**
42409      * Searches all regions for a panel with the specified id
42410      * @param {String} panelId
42411      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42412      */
42413     findPanel : function(panelId){
42414         var rs = this.regions;
42415         for(var target in rs){
42416             if(typeof rs[target] != "function"){
42417                 var p = rs[target].getPanel(panelId);
42418                 if(p){
42419                     return p;
42420                 }
42421             }
42422         }
42423         return null;
42424     },
42425
42426     /**
42427      * Searches all regions for a panel with the specified id and activates (shows) it.
42428      * @param {String/ContentPanel} panelId The panels id or the panel itself
42429      * @return {Roo.ContentPanel} The shown panel or null
42430      */
42431     showPanel : function(panelId) {
42432       var rs = this.regions;
42433       for(var target in rs){
42434          var r = rs[target];
42435          if(typeof r != "function"){
42436             if(r.hasPanel(panelId)){
42437                return r.showPanel(panelId);
42438             }
42439          }
42440       }
42441       return null;
42442    },
42443
42444    /**
42445      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42446      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42447      */
42448    /*
42449     restoreState : function(provider){
42450         if(!provider){
42451             provider = Roo.state.Manager;
42452         }
42453         var sm = new Roo.LayoutStateManager();
42454         sm.init(this, provider);
42455     },
42456 */
42457  
42458  
42459     /**
42460      * Adds a xtype elements to the layout.
42461      * <pre><code>
42462
42463 layout.addxtype({
42464        xtype : 'ContentPanel',
42465        region: 'west',
42466        items: [ .... ]
42467    }
42468 );
42469
42470 layout.addxtype({
42471         xtype : 'NestedLayoutPanel',
42472         region: 'west',
42473         layout: {
42474            center: { },
42475            west: { }   
42476         },
42477         items : [ ... list of content panels or nested layout panels.. ]
42478    }
42479 );
42480 </code></pre>
42481      * @param {Object} cfg Xtype definition of item to add.
42482      */
42483     addxtype : function(cfg)
42484     {
42485         // basically accepts a pannel...
42486         // can accept a layout region..!?!?
42487         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42488         
42489         
42490         // theory?  children can only be panels??
42491         
42492         //if (!cfg.xtype.match(/Panel$/)) {
42493         //    return false;
42494         //}
42495         var ret = false;
42496         
42497         if (typeof(cfg.region) == 'undefined') {
42498             Roo.log("Failed to add Panel, region was not set");
42499             Roo.log(cfg);
42500             return false;
42501         }
42502         var region = cfg.region;
42503         delete cfg.region;
42504         
42505           
42506         var xitems = [];
42507         if (cfg.items) {
42508             xitems = cfg.items;
42509             delete cfg.items;
42510         }
42511         var nb = false;
42512         
42513         if ( region == 'center') {
42514             Roo.log("Center: " + cfg.title);
42515         }
42516         
42517         
42518         switch(cfg.xtype) 
42519         {
42520             case 'Content':  // ContentPanel (el, cfg)
42521             case 'Scroll':  // ContentPanel (el, cfg)
42522             case 'View': 
42523                 cfg.autoCreate = cfg.autoCreate || true;
42524                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42525                 //} else {
42526                 //    var el = this.el.createChild();
42527                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42528                 //}
42529                 
42530                 this.add(region, ret);
42531                 break;
42532             
42533             /*
42534             case 'TreePanel': // our new panel!
42535                 cfg.el = this.el.createChild();
42536                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42537                 this.add(region, ret);
42538                 break;
42539             */
42540             
42541             case 'Nest': 
42542                 // create a new Layout (which is  a Border Layout...
42543                 
42544                 var clayout = cfg.layout;
42545                 clayout.el  = this.el.createChild();
42546                 clayout.items   = clayout.items  || [];
42547                 
42548                 delete cfg.layout;
42549                 
42550                 // replace this exitems with the clayout ones..
42551                 xitems = clayout.items;
42552                  
42553                 // force background off if it's in center...
42554                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42555                     cfg.background = false;
42556                 }
42557                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
42558                 
42559                 
42560                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42561                 //console.log('adding nested layout panel '  + cfg.toSource());
42562                 this.add(region, ret);
42563                 nb = {}; /// find first...
42564                 break;
42565             
42566             case 'Grid':
42567                 
42568                 // needs grid and region
42569                 
42570                 //var el = this.getRegion(region).el.createChild();
42571                 /*
42572                  *var el = this.el.createChild();
42573                 // create the grid first...
42574                 cfg.grid.container = el;
42575                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
42576                 */
42577                 
42578                 if (region == 'center' && this.active ) {
42579                     cfg.background = false;
42580                 }
42581                 
42582                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42583                 
42584                 this.add(region, ret);
42585                 /*
42586                 if (cfg.background) {
42587                     // render grid on panel activation (if panel background)
42588                     ret.on('activate', function(gp) {
42589                         if (!gp.grid.rendered) {
42590                     //        gp.grid.render(el);
42591                         }
42592                     });
42593                 } else {
42594                   //  cfg.grid.render(el);
42595                 }
42596                 */
42597                 break;
42598            
42599            
42600             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
42601                 // it was the old xcomponent building that caused this before.
42602                 // espeically if border is the top element in the tree.
42603                 ret = this;
42604                 break; 
42605                 
42606                     
42607                 
42608                 
42609                 
42610             default:
42611                 /*
42612                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
42613                     
42614                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42615                     this.add(region, ret);
42616                 } else {
42617                 */
42618                     Roo.log(cfg);
42619                     throw "Can not add '" + cfg.xtype + "' to Border";
42620                     return null;
42621              
42622                                 
42623              
42624         }
42625         this.beginUpdate();
42626         // add children..
42627         var region = '';
42628         var abn = {};
42629         Roo.each(xitems, function(i)  {
42630             region = nb && i.region ? i.region : false;
42631             
42632             var add = ret.addxtype(i);
42633            
42634             if (region) {
42635                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
42636                 if (!i.background) {
42637                     abn[region] = nb[region] ;
42638                 }
42639             }
42640             
42641         });
42642         this.endUpdate();
42643
42644         // make the last non-background panel active..
42645         //if (nb) { Roo.log(abn); }
42646         if (nb) {
42647             
42648             for(var r in abn) {
42649                 region = this.getRegion(r);
42650                 if (region) {
42651                     // tried using nb[r], but it does not work..
42652                      
42653                     region.showPanel(abn[r]);
42654                    
42655                 }
42656             }
42657         }
42658         return ret;
42659         
42660     },
42661     
42662     
42663 // private
42664     factory : function(cfg)
42665     {
42666         
42667         var validRegions = Roo.bootstrap.layout.Border.regions;
42668
42669         var target = cfg.region;
42670         cfg.mgr = this;
42671         
42672         var r = Roo.bootstrap.layout;
42673         Roo.log(target);
42674         switch(target){
42675             case "north":
42676                 return new r.North(cfg);
42677             case "south":
42678                 return new r.South(cfg);
42679             case "east":
42680                 return new r.East(cfg);
42681             case "west":
42682                 return new r.West(cfg);
42683             case "center":
42684                 return new r.Center(cfg);
42685         }
42686         throw 'Layout region "'+target+'" not supported.';
42687     }
42688     
42689     
42690 });
42691  /*
42692  * Based on:
42693  * Ext JS Library 1.1.1
42694  * Copyright(c) 2006-2007, Ext JS, LLC.
42695  *
42696  * Originally Released Under LGPL - original licence link has changed is not relivant.
42697  *
42698  * Fork - LGPL
42699  * <script type="text/javascript">
42700  */
42701  
42702 /**
42703  * @class Roo.bootstrap.layout.Basic
42704  * @extends Roo.util.Observable
42705  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42706  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42707  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42708  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
42709  * @cfg {string}   region  the region that it inhabits..
42710  * @cfg {bool}   skipConfig skip config?
42711  * 
42712
42713  */
42714 Roo.bootstrap.layout.Basic = function(config){
42715     
42716     this.mgr = config.mgr;
42717     
42718     this.position = config.region;
42719     
42720     var skipConfig = config.skipConfig;
42721     
42722     this.events = {
42723         /**
42724          * @scope Roo.BasicLayoutRegion
42725          */
42726         
42727         /**
42728          * @event beforeremove
42729          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42730          * @param {Roo.LayoutRegion} this
42731          * @param {Roo.ContentPanel} panel The panel
42732          * @param {Object} e The cancel event object
42733          */
42734         "beforeremove" : true,
42735         /**
42736          * @event invalidated
42737          * Fires when the layout for this region is changed.
42738          * @param {Roo.LayoutRegion} this
42739          */
42740         "invalidated" : true,
42741         /**
42742          * @event visibilitychange
42743          * Fires when this region is shown or hidden 
42744          * @param {Roo.LayoutRegion} this
42745          * @param {Boolean} visibility true or false
42746          */
42747         "visibilitychange" : true,
42748         /**
42749          * @event paneladded
42750          * Fires when a panel is added. 
42751          * @param {Roo.LayoutRegion} this
42752          * @param {Roo.ContentPanel} panel The panel
42753          */
42754         "paneladded" : true,
42755         /**
42756          * @event panelremoved
42757          * Fires when a panel is removed. 
42758          * @param {Roo.LayoutRegion} this
42759          * @param {Roo.ContentPanel} panel The panel
42760          */
42761         "panelremoved" : true,
42762         /**
42763          * @event beforecollapse
42764          * Fires when this region before collapse.
42765          * @param {Roo.LayoutRegion} this
42766          */
42767         "beforecollapse" : true,
42768         /**
42769          * @event collapsed
42770          * Fires when this region is collapsed.
42771          * @param {Roo.LayoutRegion} this
42772          */
42773         "collapsed" : true,
42774         /**
42775          * @event expanded
42776          * Fires when this region is expanded.
42777          * @param {Roo.LayoutRegion} this
42778          */
42779         "expanded" : true,
42780         /**
42781          * @event slideshow
42782          * Fires when this region is slid into view.
42783          * @param {Roo.LayoutRegion} this
42784          */
42785         "slideshow" : true,
42786         /**
42787          * @event slidehide
42788          * Fires when this region slides out of view. 
42789          * @param {Roo.LayoutRegion} this
42790          */
42791         "slidehide" : true,
42792         /**
42793          * @event panelactivated
42794          * Fires when a panel is activated. 
42795          * @param {Roo.LayoutRegion} this
42796          * @param {Roo.ContentPanel} panel The activated panel
42797          */
42798         "panelactivated" : true,
42799         /**
42800          * @event resized
42801          * Fires when the user resizes this region. 
42802          * @param {Roo.LayoutRegion} this
42803          * @param {Number} newSize The new size (width for east/west, height for north/south)
42804          */
42805         "resized" : true
42806     };
42807     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42808     this.panels = new Roo.util.MixedCollection();
42809     this.panels.getKey = this.getPanelId.createDelegate(this);
42810     this.box = null;
42811     this.activePanel = null;
42812     // ensure listeners are added...
42813     
42814     if (config.listeners || config.events) {
42815         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
42816             listeners : config.listeners || {},
42817             events : config.events || {}
42818         });
42819     }
42820     
42821     if(skipConfig !== true){
42822         this.applyConfig(config);
42823     }
42824 };
42825
42826 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
42827 {
42828     getPanelId : function(p){
42829         return p.getId();
42830     },
42831     
42832     applyConfig : function(config){
42833         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42834         this.config = config;
42835         
42836     },
42837     
42838     /**
42839      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42840      * the width, for horizontal (north, south) the height.
42841      * @param {Number} newSize The new width or height
42842      */
42843     resizeTo : function(newSize){
42844         var el = this.el ? this.el :
42845                  (this.activePanel ? this.activePanel.getEl() : null);
42846         if(el){
42847             switch(this.position){
42848                 case "east":
42849                 case "west":
42850                     el.setWidth(newSize);
42851                     this.fireEvent("resized", this, newSize);
42852                 break;
42853                 case "north":
42854                 case "south":
42855                     el.setHeight(newSize);
42856                     this.fireEvent("resized", this, newSize);
42857                 break;                
42858             }
42859         }
42860     },
42861     
42862     getBox : function(){
42863         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42864     },
42865     
42866     getMargins : function(){
42867         return this.margins;
42868     },
42869     
42870     updateBox : function(box){
42871         this.box = box;
42872         var el = this.activePanel.getEl();
42873         el.dom.style.left = box.x + "px";
42874         el.dom.style.top = box.y + "px";
42875         this.activePanel.setSize(box.width, box.height);
42876     },
42877     
42878     /**
42879      * Returns the container element for this region.
42880      * @return {Roo.Element}
42881      */
42882     getEl : function(){
42883         return this.activePanel;
42884     },
42885     
42886     /**
42887      * Returns true if this region is currently visible.
42888      * @return {Boolean}
42889      */
42890     isVisible : function(){
42891         return this.activePanel ? true : false;
42892     },
42893     
42894     setActivePanel : function(panel){
42895         panel = this.getPanel(panel);
42896         if(this.activePanel && this.activePanel != panel){
42897             this.activePanel.setActiveState(false);
42898             this.activePanel.getEl().setLeftTop(-10000,-10000);
42899         }
42900         this.activePanel = panel;
42901         panel.setActiveState(true);
42902         if(this.box){
42903             panel.setSize(this.box.width, this.box.height);
42904         }
42905         this.fireEvent("panelactivated", this, panel);
42906         this.fireEvent("invalidated");
42907     },
42908     
42909     /**
42910      * Show the specified panel.
42911      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42912      * @return {Roo.ContentPanel} The shown panel or null
42913      */
42914     showPanel : function(panel){
42915         panel = this.getPanel(panel);
42916         if(panel){
42917             this.setActivePanel(panel);
42918         }
42919         return panel;
42920     },
42921     
42922     /**
42923      * Get the active panel for this region.
42924      * @return {Roo.ContentPanel} The active panel or null
42925      */
42926     getActivePanel : function(){
42927         return this.activePanel;
42928     },
42929     
42930     /**
42931      * Add the passed ContentPanel(s)
42932      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42933      * @return {Roo.ContentPanel} The panel added (if only one was added)
42934      */
42935     add : function(panel){
42936         if(arguments.length > 1){
42937             for(var i = 0, len = arguments.length; i < len; i++) {
42938                 this.add(arguments[i]);
42939             }
42940             return null;
42941         }
42942         if(this.hasPanel(panel)){
42943             this.showPanel(panel);
42944             return panel;
42945         }
42946         var el = panel.getEl();
42947         if(el.dom.parentNode != this.mgr.el.dom){
42948             this.mgr.el.dom.appendChild(el.dom);
42949         }
42950         if(panel.setRegion){
42951             panel.setRegion(this);
42952         }
42953         this.panels.add(panel);
42954         el.setStyle("position", "absolute");
42955         if(!panel.background){
42956             this.setActivePanel(panel);
42957             if(this.config.initialSize && this.panels.getCount()==1){
42958                 this.resizeTo(this.config.initialSize);
42959             }
42960         }
42961         this.fireEvent("paneladded", this, panel);
42962         return panel;
42963     },
42964     
42965     /**
42966      * Returns true if the panel is in this region.
42967      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42968      * @return {Boolean}
42969      */
42970     hasPanel : function(panel){
42971         if(typeof panel == "object"){ // must be panel obj
42972             panel = panel.getId();
42973         }
42974         return this.getPanel(panel) ? true : false;
42975     },
42976     
42977     /**
42978      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42979      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42980      * @param {Boolean} preservePanel Overrides the config preservePanel option
42981      * @return {Roo.ContentPanel} The panel that was removed
42982      */
42983     remove : function(panel, preservePanel){
42984         panel = this.getPanel(panel);
42985         if(!panel){
42986             return null;
42987         }
42988         var e = {};
42989         this.fireEvent("beforeremove", this, panel, e);
42990         if(e.cancel === true){
42991             return null;
42992         }
42993         var panelId = panel.getId();
42994         this.panels.removeKey(panelId);
42995         return panel;
42996     },
42997     
42998     /**
42999      * Returns the panel specified or null if it's not in this region.
43000      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43001      * @return {Roo.ContentPanel}
43002      */
43003     getPanel : function(id){
43004         if(typeof id == "object"){ // must be panel obj
43005             return id;
43006         }
43007         return this.panels.get(id);
43008     },
43009     
43010     /**
43011      * Returns this regions position (north/south/east/west/center).
43012      * @return {String} 
43013      */
43014     getPosition: function(){
43015         return this.position;    
43016     }
43017 });/*
43018  * Based on:
43019  * Ext JS Library 1.1.1
43020  * Copyright(c) 2006-2007, Ext JS, LLC.
43021  *
43022  * Originally Released Under LGPL - original licence link has changed is not relivant.
43023  *
43024  * Fork - LGPL
43025  * <script type="text/javascript">
43026  */
43027  
43028 /**
43029  * @class Roo.bootstrap.layout.Region
43030  * @extends Roo.bootstrap.layout.Basic
43031  * This class represents a region in a layout manager.
43032  
43033  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43034  * @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})
43035  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43036  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43037  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43038  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43039  * @cfg {String}    title           The title for the region (overrides panel titles)
43040  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43041  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43042  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43043  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43044  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43045  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43046  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43047  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43048  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43049  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43050
43051  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43052  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43053  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43054  * @cfg {Number}    width           For East/West panels
43055  * @cfg {Number}    height          For North/South panels
43056  * @cfg {Boolean}   split           To show the splitter
43057  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43058  * 
43059  * @cfg {string}   cls             Extra CSS classes to add to region
43060  * 
43061  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43062  * @cfg {string}   region  the region that it inhabits..
43063  *
43064
43065  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43066  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43067
43068  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43069  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43070  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43071  */
43072 Roo.bootstrap.layout.Region = function(config)
43073 {
43074     this.applyConfig(config);
43075
43076     var mgr = config.mgr;
43077     var pos = config.region;
43078     config.skipConfig = true;
43079     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43080     
43081     if (mgr.el) {
43082         this.onRender(mgr.el);   
43083     }
43084      
43085     this.visible = true;
43086     this.collapsed = false;
43087     this.unrendered_panels = [];
43088 };
43089
43090 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43091
43092     position: '', // set by wrapper (eg. north/south etc..)
43093     unrendered_panels : null,  // unrendered panels.
43094     
43095     tabPosition : false,
43096     
43097     mgr: false, // points to 'Border'
43098     
43099     
43100     createBody : function(){
43101         /** This region's body element 
43102         * @type Roo.Element */
43103         this.bodyEl = this.el.createChild({
43104                 tag: "div",
43105                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43106         });
43107     },
43108
43109     onRender: function(ctr, pos)
43110     {
43111         var dh = Roo.DomHelper;
43112         /** This region's container element 
43113         * @type Roo.Element */
43114         this.el = dh.append(ctr.dom, {
43115                 tag: "div",
43116                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43117             }, true);
43118         /** This region's title element 
43119         * @type Roo.Element */
43120     
43121         this.titleEl = dh.append(this.el.dom,  {
43122                 tag: "div",
43123                 unselectable: "on",
43124                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43125                 children:[
43126                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43127                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43128                 ]
43129             }, true);
43130         
43131         this.titleEl.enableDisplayMode();
43132         /** This region's title text element 
43133         * @type HTMLElement */
43134         this.titleTextEl = this.titleEl.dom.firstChild;
43135         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43136         /*
43137         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43138         this.closeBtn.enableDisplayMode();
43139         this.closeBtn.on("click", this.closeClicked, this);
43140         this.closeBtn.hide();
43141     */
43142         this.createBody(this.config);
43143         if(this.config.hideWhenEmpty){
43144             this.hide();
43145             this.on("paneladded", this.validateVisibility, this);
43146             this.on("panelremoved", this.validateVisibility, this);
43147         }
43148         if(this.autoScroll){
43149             this.bodyEl.setStyle("overflow", "auto");
43150         }else{
43151             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43152         }
43153         //if(c.titlebar !== false){
43154             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43155                 this.titleEl.hide();
43156             }else{
43157                 this.titleEl.show();
43158                 if(this.config.title){
43159                     this.titleTextEl.innerHTML = this.config.title;
43160                 }
43161             }
43162         //}
43163         if(this.config.collapsed){
43164             this.collapse(true);
43165         }
43166         if(this.config.hidden){
43167             this.hide();
43168         }
43169         
43170         if (this.unrendered_panels && this.unrendered_panels.length) {
43171             for (var i =0;i< this.unrendered_panels.length; i++) {
43172                 this.add(this.unrendered_panels[i]);
43173             }
43174             this.unrendered_panels = null;
43175             
43176         }
43177         
43178     },
43179     
43180     applyConfig : function(c)
43181     {
43182         /*
43183          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43184             var dh = Roo.DomHelper;
43185             if(c.titlebar !== false){
43186                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43187                 this.collapseBtn.on("click", this.collapse, this);
43188                 this.collapseBtn.enableDisplayMode();
43189                 /*
43190                 if(c.showPin === true || this.showPin){
43191                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43192                     this.stickBtn.enableDisplayMode();
43193                     this.stickBtn.on("click", this.expand, this);
43194                     this.stickBtn.hide();
43195                 }
43196                 
43197             }
43198             */
43199             /** This region's collapsed element
43200             * @type Roo.Element */
43201             /*
43202              *
43203             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43204                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43205             ]}, true);
43206             
43207             if(c.floatable !== false){
43208                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43209                this.collapsedEl.on("click", this.collapseClick, this);
43210             }
43211
43212             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43213                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43214                    id: "message", unselectable: "on", style:{"float":"left"}});
43215                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43216              }
43217             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43218             this.expandBtn.on("click", this.expand, this);
43219             
43220         }
43221         
43222         if(this.collapseBtn){
43223             this.collapseBtn.setVisible(c.collapsible == true);
43224         }
43225         
43226         this.cmargins = c.cmargins || this.cmargins ||
43227                          (this.position == "west" || this.position == "east" ?
43228                              {top: 0, left: 2, right:2, bottom: 0} :
43229                              {top: 2, left: 0, right:0, bottom: 2});
43230         */
43231         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43232         
43233         
43234         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43235         
43236         this.autoScroll = c.autoScroll || false;
43237         
43238         
43239        
43240         
43241         this.duration = c.duration || .30;
43242         this.slideDuration = c.slideDuration || .45;
43243         this.config = c;
43244        
43245     },
43246     /**
43247      * Returns true if this region is currently visible.
43248      * @return {Boolean}
43249      */
43250     isVisible : function(){
43251         return this.visible;
43252     },
43253
43254     /**
43255      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43256      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43257      */
43258     //setCollapsedTitle : function(title){
43259     //    title = title || "&#160;";
43260      //   if(this.collapsedTitleTextEl){
43261       //      this.collapsedTitleTextEl.innerHTML = title;
43262        // }
43263     //},
43264
43265     getBox : function(){
43266         var b;
43267       //  if(!this.collapsed){
43268             b = this.el.getBox(false, true);
43269        // }else{
43270           //  b = this.collapsedEl.getBox(false, true);
43271         //}
43272         return b;
43273     },
43274
43275     getMargins : function(){
43276         return this.margins;
43277         //return this.collapsed ? this.cmargins : this.margins;
43278     },
43279 /*
43280     highlight : function(){
43281         this.el.addClass("x-layout-panel-dragover");
43282     },
43283
43284     unhighlight : function(){
43285         this.el.removeClass("x-layout-panel-dragover");
43286     },
43287 */
43288     updateBox : function(box)
43289     {
43290         if (!this.bodyEl) {
43291             return; // not rendered yet..
43292         }
43293         
43294         this.box = box;
43295         if(!this.collapsed){
43296             this.el.dom.style.left = box.x + "px";
43297             this.el.dom.style.top = box.y + "px";
43298             this.updateBody(box.width, box.height);
43299         }else{
43300             this.collapsedEl.dom.style.left = box.x + "px";
43301             this.collapsedEl.dom.style.top = box.y + "px";
43302             this.collapsedEl.setSize(box.width, box.height);
43303         }
43304         if(this.tabs){
43305             this.tabs.autoSizeTabs();
43306         }
43307     },
43308
43309     updateBody : function(w, h)
43310     {
43311         if(w !== null){
43312             this.el.setWidth(w);
43313             w -= this.el.getBorderWidth("rl");
43314             if(this.config.adjustments){
43315                 w += this.config.adjustments[0];
43316             }
43317         }
43318         if(h !== null && h > 0){
43319             this.el.setHeight(h);
43320             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43321             h -= this.el.getBorderWidth("tb");
43322             if(this.config.adjustments){
43323                 h += this.config.adjustments[1];
43324             }
43325             this.bodyEl.setHeight(h);
43326             if(this.tabs){
43327                 h = this.tabs.syncHeight(h);
43328             }
43329         }
43330         if(this.panelSize){
43331             w = w !== null ? w : this.panelSize.width;
43332             h = h !== null ? h : this.panelSize.height;
43333         }
43334         if(this.activePanel){
43335             var el = this.activePanel.getEl();
43336             w = w !== null ? w : el.getWidth();
43337             h = h !== null ? h : el.getHeight();
43338             this.panelSize = {width: w, height: h};
43339             this.activePanel.setSize(w, h);
43340         }
43341         if(Roo.isIE && this.tabs){
43342             this.tabs.el.repaint();
43343         }
43344     },
43345
43346     /**
43347      * Returns the container element for this region.
43348      * @return {Roo.Element}
43349      */
43350     getEl : function(){
43351         return this.el;
43352     },
43353
43354     /**
43355      * Hides this region.
43356      */
43357     hide : function(){
43358         //if(!this.collapsed){
43359             this.el.dom.style.left = "-2000px";
43360             this.el.hide();
43361         //}else{
43362          //   this.collapsedEl.dom.style.left = "-2000px";
43363          //   this.collapsedEl.hide();
43364        // }
43365         this.visible = false;
43366         this.fireEvent("visibilitychange", this, false);
43367     },
43368
43369     /**
43370      * Shows this region if it was previously hidden.
43371      */
43372     show : function(){
43373         //if(!this.collapsed){
43374             this.el.show();
43375         //}else{
43376         //    this.collapsedEl.show();
43377        // }
43378         this.visible = true;
43379         this.fireEvent("visibilitychange", this, true);
43380     },
43381 /*
43382     closeClicked : function(){
43383         if(this.activePanel){
43384             this.remove(this.activePanel);
43385         }
43386     },
43387
43388     collapseClick : function(e){
43389         if(this.isSlid){
43390            e.stopPropagation();
43391            this.slideIn();
43392         }else{
43393            e.stopPropagation();
43394            this.slideOut();
43395         }
43396     },
43397 */
43398     /**
43399      * Collapses this region.
43400      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43401      */
43402     /*
43403     collapse : function(skipAnim, skipCheck = false){
43404         if(this.collapsed) {
43405             return;
43406         }
43407         
43408         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43409             
43410             this.collapsed = true;
43411             if(this.split){
43412                 this.split.el.hide();
43413             }
43414             if(this.config.animate && skipAnim !== true){
43415                 this.fireEvent("invalidated", this);
43416                 this.animateCollapse();
43417             }else{
43418                 this.el.setLocation(-20000,-20000);
43419                 this.el.hide();
43420                 this.collapsedEl.show();
43421                 this.fireEvent("collapsed", this);
43422                 this.fireEvent("invalidated", this);
43423             }
43424         }
43425         
43426     },
43427 */
43428     animateCollapse : function(){
43429         // overridden
43430     },
43431
43432     /**
43433      * Expands this region if it was previously collapsed.
43434      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43435      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43436      */
43437     /*
43438     expand : function(e, skipAnim){
43439         if(e) {
43440             e.stopPropagation();
43441         }
43442         if(!this.collapsed || this.el.hasActiveFx()) {
43443             return;
43444         }
43445         if(this.isSlid){
43446             this.afterSlideIn();
43447             skipAnim = true;
43448         }
43449         this.collapsed = false;
43450         if(this.config.animate && skipAnim !== true){
43451             this.animateExpand();
43452         }else{
43453             this.el.show();
43454             if(this.split){
43455                 this.split.el.show();
43456             }
43457             this.collapsedEl.setLocation(-2000,-2000);
43458             this.collapsedEl.hide();
43459             this.fireEvent("invalidated", this);
43460             this.fireEvent("expanded", this);
43461         }
43462     },
43463 */
43464     animateExpand : function(){
43465         // overridden
43466     },
43467
43468     initTabs : function()
43469     {
43470         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43471         
43472         var ts = new Roo.bootstrap.panel.Tabs({
43473             el: this.bodyEl.dom,
43474             region : this,
43475             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
43476             disableTooltips: this.config.disableTabTips,
43477             toolbar : this.config.toolbar
43478         });
43479         
43480         if(this.config.hideTabs){
43481             ts.stripWrap.setDisplayed(false);
43482         }
43483         this.tabs = ts;
43484         ts.resizeTabs = this.config.resizeTabs === true;
43485         ts.minTabWidth = this.config.minTabWidth || 40;
43486         ts.maxTabWidth = this.config.maxTabWidth || 250;
43487         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43488         ts.monitorResize = false;
43489         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43490         ts.bodyEl.addClass('roo-layout-tabs-body');
43491         this.panels.each(this.initPanelAsTab, this);
43492     },
43493
43494     initPanelAsTab : function(panel){
43495         var ti = this.tabs.addTab(
43496             panel.getEl().id,
43497             panel.getTitle(),
43498             null,
43499             this.config.closeOnTab && panel.isClosable(),
43500             panel.tpl
43501         );
43502         if(panel.tabTip !== undefined){
43503             ti.setTooltip(panel.tabTip);
43504         }
43505         ti.on("activate", function(){
43506               this.setActivePanel(panel);
43507         }, this);
43508         
43509         if(this.config.closeOnTab){
43510             ti.on("beforeclose", function(t, e){
43511                 e.cancel = true;
43512                 this.remove(panel);
43513             }, this);
43514         }
43515         
43516         panel.tabItem = ti;
43517         
43518         return ti;
43519     },
43520
43521     updatePanelTitle : function(panel, title)
43522     {
43523         if(this.activePanel == panel){
43524             this.updateTitle(title);
43525         }
43526         if(this.tabs){
43527             var ti = this.tabs.getTab(panel.getEl().id);
43528             ti.setText(title);
43529             if(panel.tabTip !== undefined){
43530                 ti.setTooltip(panel.tabTip);
43531             }
43532         }
43533     },
43534
43535     updateTitle : function(title){
43536         if(this.titleTextEl && !this.config.title){
43537             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43538         }
43539     },
43540
43541     setActivePanel : function(panel)
43542     {
43543         panel = this.getPanel(panel);
43544         if(this.activePanel && this.activePanel != panel){
43545             if(this.activePanel.setActiveState(false) === false){
43546                 return;
43547             }
43548         }
43549         this.activePanel = panel;
43550         panel.setActiveState(true);
43551         if(this.panelSize){
43552             panel.setSize(this.panelSize.width, this.panelSize.height);
43553         }
43554         if(this.closeBtn){
43555             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43556         }
43557         this.updateTitle(panel.getTitle());
43558         if(this.tabs){
43559             this.fireEvent("invalidated", this);
43560         }
43561         this.fireEvent("panelactivated", this, panel);
43562     },
43563
43564     /**
43565      * Shows the specified panel.
43566      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43567      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43568      */
43569     showPanel : function(panel)
43570     {
43571         panel = this.getPanel(panel);
43572         if(panel){
43573             if(this.tabs){
43574                 var tab = this.tabs.getTab(panel.getEl().id);
43575                 if(tab.isHidden()){
43576                     this.tabs.unhideTab(tab.id);
43577                 }
43578                 tab.activate();
43579             }else{
43580                 this.setActivePanel(panel);
43581             }
43582         }
43583         return panel;
43584     },
43585
43586     /**
43587      * Get the active panel for this region.
43588      * @return {Roo.ContentPanel} The active panel or null
43589      */
43590     getActivePanel : function(){
43591         return this.activePanel;
43592     },
43593
43594     validateVisibility : function(){
43595         if(this.panels.getCount() < 1){
43596             this.updateTitle("&#160;");
43597             this.closeBtn.hide();
43598             this.hide();
43599         }else{
43600             if(!this.isVisible()){
43601                 this.show();
43602             }
43603         }
43604     },
43605
43606     /**
43607      * Adds the passed ContentPanel(s) to this region.
43608      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43609      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43610      */
43611     add : function(panel)
43612     {
43613         if(arguments.length > 1){
43614             for(var i = 0, len = arguments.length; i < len; i++) {
43615                 this.add(arguments[i]);
43616             }
43617             return null;
43618         }
43619         
43620         // if we have not been rendered yet, then we can not really do much of this..
43621         if (!this.bodyEl) {
43622             this.unrendered_panels.push(panel);
43623             return panel;
43624         }
43625         
43626         
43627         
43628         
43629         if(this.hasPanel(panel)){
43630             this.showPanel(panel);
43631             return panel;
43632         }
43633         panel.setRegion(this);
43634         this.panels.add(panel);
43635        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43636             // sinle panel - no tab...?? would it not be better to render it with the tabs,
43637             // and hide them... ???
43638             this.bodyEl.dom.appendChild(panel.getEl().dom);
43639             if(panel.background !== true){
43640                 this.setActivePanel(panel);
43641             }
43642             this.fireEvent("paneladded", this, panel);
43643             return panel;
43644         }
43645         */
43646         if(!this.tabs){
43647             this.initTabs();
43648         }else{
43649             this.initPanelAsTab(panel);
43650         }
43651         
43652         
43653         if(panel.background !== true){
43654             this.tabs.activate(panel.getEl().id);
43655         }
43656         this.fireEvent("paneladded", this, panel);
43657         return panel;
43658     },
43659
43660     /**
43661      * Hides the tab for the specified panel.
43662      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43663      */
43664     hidePanel : function(panel){
43665         if(this.tabs && (panel = this.getPanel(panel))){
43666             this.tabs.hideTab(panel.getEl().id);
43667         }
43668     },
43669
43670     /**
43671      * Unhides the tab for a previously hidden panel.
43672      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43673      */
43674     unhidePanel : function(panel){
43675         if(this.tabs && (panel = this.getPanel(panel))){
43676             this.tabs.unhideTab(panel.getEl().id);
43677         }
43678     },
43679
43680     clearPanels : function(){
43681         while(this.panels.getCount() > 0){
43682              this.remove(this.panels.first());
43683         }
43684     },
43685
43686     /**
43687      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43688      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43689      * @param {Boolean} preservePanel Overrides the config preservePanel option
43690      * @return {Roo.ContentPanel} The panel that was removed
43691      */
43692     remove : function(panel, preservePanel)
43693     {
43694         panel = this.getPanel(panel);
43695         if(!panel){
43696             return null;
43697         }
43698         var e = {};
43699         this.fireEvent("beforeremove", this, panel, e);
43700         if(e.cancel === true){
43701             return null;
43702         }
43703         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43704         var panelId = panel.getId();
43705         this.panels.removeKey(panelId);
43706         if(preservePanel){
43707             document.body.appendChild(panel.getEl().dom);
43708         }
43709         if(this.tabs){
43710             this.tabs.removeTab(panel.getEl().id);
43711         }else if (!preservePanel){
43712             this.bodyEl.dom.removeChild(panel.getEl().dom);
43713         }
43714         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43715             var p = this.panels.first();
43716             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43717             tempEl.appendChild(p.getEl().dom);
43718             this.bodyEl.update("");
43719             this.bodyEl.dom.appendChild(p.getEl().dom);
43720             tempEl = null;
43721             this.updateTitle(p.getTitle());
43722             this.tabs = null;
43723             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43724             this.setActivePanel(p);
43725         }
43726         panel.setRegion(null);
43727         if(this.activePanel == panel){
43728             this.activePanel = null;
43729         }
43730         if(this.config.autoDestroy !== false && preservePanel !== true){
43731             try{panel.destroy();}catch(e){}
43732         }
43733         this.fireEvent("panelremoved", this, panel);
43734         return panel;
43735     },
43736
43737     /**
43738      * Returns the TabPanel component used by this region
43739      * @return {Roo.TabPanel}
43740      */
43741     getTabs : function(){
43742         return this.tabs;
43743     },
43744
43745     createTool : function(parentEl, className){
43746         var btn = Roo.DomHelper.append(parentEl, {
43747             tag: "div",
43748             cls: "x-layout-tools-button",
43749             children: [ {
43750                 tag: "div",
43751                 cls: "roo-layout-tools-button-inner " + className,
43752                 html: "&#160;"
43753             }]
43754         }, true);
43755         btn.addClassOnOver("roo-layout-tools-button-over");
43756         return btn;
43757     }
43758 });/*
43759  * Based on:
43760  * Ext JS Library 1.1.1
43761  * Copyright(c) 2006-2007, Ext JS, LLC.
43762  *
43763  * Originally Released Under LGPL - original licence link has changed is not relivant.
43764  *
43765  * Fork - LGPL
43766  * <script type="text/javascript">
43767  */
43768  
43769
43770
43771 /**
43772  * @class Roo.SplitLayoutRegion
43773  * @extends Roo.LayoutRegion
43774  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43775  */
43776 Roo.bootstrap.layout.Split = function(config){
43777     this.cursor = config.cursor;
43778     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
43779 };
43780
43781 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
43782 {
43783     splitTip : "Drag to resize.",
43784     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43785     useSplitTips : false,
43786
43787     applyConfig : function(config){
43788         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
43789     },
43790     
43791     onRender : function(ctr,pos) {
43792         
43793         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
43794         if(!this.config.split){
43795             return;
43796         }
43797         if(!this.split){
43798             
43799             var splitEl = Roo.DomHelper.append(ctr.dom,  {
43800                             tag: "div",
43801                             id: this.el.id + "-split",
43802                             cls: "roo-layout-split roo-layout-split-"+this.position,
43803                             html: "&#160;"
43804             });
43805             /** The SplitBar for this region 
43806             * @type Roo.SplitBar */
43807             // does not exist yet...
43808             Roo.log([this.position, this.orientation]);
43809             
43810             this.split = new Roo.bootstrap.SplitBar({
43811                 dragElement : splitEl,
43812                 resizingElement: this.el,
43813                 orientation : this.orientation
43814             });
43815             
43816             this.split.on("moved", this.onSplitMove, this);
43817             this.split.useShim = this.config.useShim === true;
43818             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43819             if(this.useSplitTips){
43820                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43821             }
43822             //if(config.collapsible){
43823             //    this.split.el.on("dblclick", this.collapse,  this);
43824             //}
43825         }
43826         if(typeof this.config.minSize != "undefined"){
43827             this.split.minSize = this.config.minSize;
43828         }
43829         if(typeof this.config.maxSize != "undefined"){
43830             this.split.maxSize = this.config.maxSize;
43831         }
43832         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
43833             this.hideSplitter();
43834         }
43835         
43836     },
43837
43838     getHMaxSize : function(){
43839          var cmax = this.config.maxSize || 10000;
43840          var center = this.mgr.getRegion("center");
43841          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43842     },
43843
43844     getVMaxSize : function(){
43845          var cmax = this.config.maxSize || 10000;
43846          var center = this.mgr.getRegion("center");
43847          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43848     },
43849
43850     onSplitMove : function(split, newSize){
43851         this.fireEvent("resized", this, newSize);
43852     },
43853     
43854     /** 
43855      * Returns the {@link Roo.SplitBar} for this region.
43856      * @return {Roo.SplitBar}
43857      */
43858     getSplitBar : function(){
43859         return this.split;
43860     },
43861     
43862     hide : function(){
43863         this.hideSplitter();
43864         Roo.bootstrap.layout.Split.superclass.hide.call(this);
43865     },
43866
43867     hideSplitter : function(){
43868         if(this.split){
43869             this.split.el.setLocation(-2000,-2000);
43870             this.split.el.hide();
43871         }
43872     },
43873
43874     show : function(){
43875         if(this.split){
43876             this.split.el.show();
43877         }
43878         Roo.bootstrap.layout.Split.superclass.show.call(this);
43879     },
43880     
43881     beforeSlide: function(){
43882         if(Roo.isGecko){// firefox overflow auto bug workaround
43883             this.bodyEl.clip();
43884             if(this.tabs) {
43885                 this.tabs.bodyEl.clip();
43886             }
43887             if(this.activePanel){
43888                 this.activePanel.getEl().clip();
43889                 
43890                 if(this.activePanel.beforeSlide){
43891                     this.activePanel.beforeSlide();
43892                 }
43893             }
43894         }
43895     },
43896     
43897     afterSlide : function(){
43898         if(Roo.isGecko){// firefox overflow auto bug workaround
43899             this.bodyEl.unclip();
43900             if(this.tabs) {
43901                 this.tabs.bodyEl.unclip();
43902             }
43903             if(this.activePanel){
43904                 this.activePanel.getEl().unclip();
43905                 if(this.activePanel.afterSlide){
43906                     this.activePanel.afterSlide();
43907                 }
43908             }
43909         }
43910     },
43911
43912     initAutoHide : function(){
43913         if(this.autoHide !== false){
43914             if(!this.autoHideHd){
43915                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43916                 this.autoHideHd = {
43917                     "mouseout": function(e){
43918                         if(!e.within(this.el, true)){
43919                             st.delay(500);
43920                         }
43921                     },
43922                     "mouseover" : function(e){
43923                         st.cancel();
43924                     },
43925                     scope : this
43926                 };
43927             }
43928             this.el.on(this.autoHideHd);
43929         }
43930     },
43931
43932     clearAutoHide : function(){
43933         if(this.autoHide !== false){
43934             this.el.un("mouseout", this.autoHideHd.mouseout);
43935             this.el.un("mouseover", this.autoHideHd.mouseover);
43936         }
43937     },
43938
43939     clearMonitor : function(){
43940         Roo.get(document).un("click", this.slideInIf, this);
43941     },
43942
43943     // these names are backwards but not changed for compat
43944     slideOut : function(){
43945         if(this.isSlid || this.el.hasActiveFx()){
43946             return;
43947         }
43948         this.isSlid = true;
43949         if(this.collapseBtn){
43950             this.collapseBtn.hide();
43951         }
43952         this.closeBtnState = this.closeBtn.getStyle('display');
43953         this.closeBtn.hide();
43954         if(this.stickBtn){
43955             this.stickBtn.show();
43956         }
43957         this.el.show();
43958         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43959         this.beforeSlide();
43960         this.el.setStyle("z-index", 10001);
43961         this.el.slideIn(this.getSlideAnchor(), {
43962             callback: function(){
43963                 this.afterSlide();
43964                 this.initAutoHide();
43965                 Roo.get(document).on("click", this.slideInIf, this);
43966                 this.fireEvent("slideshow", this);
43967             },
43968             scope: this,
43969             block: true
43970         });
43971     },
43972
43973     afterSlideIn : function(){
43974         this.clearAutoHide();
43975         this.isSlid = false;
43976         this.clearMonitor();
43977         this.el.setStyle("z-index", "");
43978         if(this.collapseBtn){
43979             this.collapseBtn.show();
43980         }
43981         this.closeBtn.setStyle('display', this.closeBtnState);
43982         if(this.stickBtn){
43983             this.stickBtn.hide();
43984         }
43985         this.fireEvent("slidehide", this);
43986     },
43987
43988     slideIn : function(cb){
43989         if(!this.isSlid || this.el.hasActiveFx()){
43990             Roo.callback(cb);
43991             return;
43992         }
43993         this.isSlid = false;
43994         this.beforeSlide();
43995         this.el.slideOut(this.getSlideAnchor(), {
43996             callback: function(){
43997                 this.el.setLeftTop(-10000, -10000);
43998                 this.afterSlide();
43999                 this.afterSlideIn();
44000                 Roo.callback(cb);
44001             },
44002             scope: this,
44003             block: true
44004         });
44005     },
44006     
44007     slideInIf : function(e){
44008         if(!e.within(this.el)){
44009             this.slideIn();
44010         }
44011     },
44012
44013     animateCollapse : function(){
44014         this.beforeSlide();
44015         this.el.setStyle("z-index", 20000);
44016         var anchor = this.getSlideAnchor();
44017         this.el.slideOut(anchor, {
44018             callback : function(){
44019                 this.el.setStyle("z-index", "");
44020                 this.collapsedEl.slideIn(anchor, {duration:.3});
44021                 this.afterSlide();
44022                 this.el.setLocation(-10000,-10000);
44023                 this.el.hide();
44024                 this.fireEvent("collapsed", this);
44025             },
44026             scope: this,
44027             block: true
44028         });
44029     },
44030
44031     animateExpand : function(){
44032         this.beforeSlide();
44033         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44034         this.el.setStyle("z-index", 20000);
44035         this.collapsedEl.hide({
44036             duration:.1
44037         });
44038         this.el.slideIn(this.getSlideAnchor(), {
44039             callback : function(){
44040                 this.el.setStyle("z-index", "");
44041                 this.afterSlide();
44042                 if(this.split){
44043                     this.split.el.show();
44044                 }
44045                 this.fireEvent("invalidated", this);
44046                 this.fireEvent("expanded", this);
44047             },
44048             scope: this,
44049             block: true
44050         });
44051     },
44052
44053     anchors : {
44054         "west" : "left",
44055         "east" : "right",
44056         "north" : "top",
44057         "south" : "bottom"
44058     },
44059
44060     sanchors : {
44061         "west" : "l",
44062         "east" : "r",
44063         "north" : "t",
44064         "south" : "b"
44065     },
44066
44067     canchors : {
44068         "west" : "tl-tr",
44069         "east" : "tr-tl",
44070         "north" : "tl-bl",
44071         "south" : "bl-tl"
44072     },
44073
44074     getAnchor : function(){
44075         return this.anchors[this.position];
44076     },
44077
44078     getCollapseAnchor : function(){
44079         return this.canchors[this.position];
44080     },
44081
44082     getSlideAnchor : function(){
44083         return this.sanchors[this.position];
44084     },
44085
44086     getAlignAdj : function(){
44087         var cm = this.cmargins;
44088         switch(this.position){
44089             case "west":
44090                 return [0, 0];
44091             break;
44092             case "east":
44093                 return [0, 0];
44094             break;
44095             case "north":
44096                 return [0, 0];
44097             break;
44098             case "south":
44099                 return [0, 0];
44100             break;
44101         }
44102     },
44103
44104     getExpandAdj : function(){
44105         var c = this.collapsedEl, cm = this.cmargins;
44106         switch(this.position){
44107             case "west":
44108                 return [-(cm.right+c.getWidth()+cm.left), 0];
44109             break;
44110             case "east":
44111                 return [cm.right+c.getWidth()+cm.left, 0];
44112             break;
44113             case "north":
44114                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44115             break;
44116             case "south":
44117                 return [0, cm.top+cm.bottom+c.getHeight()];
44118             break;
44119         }
44120     }
44121 });/*
44122  * Based on:
44123  * Ext JS Library 1.1.1
44124  * Copyright(c) 2006-2007, Ext JS, LLC.
44125  *
44126  * Originally Released Under LGPL - original licence link has changed is not relivant.
44127  *
44128  * Fork - LGPL
44129  * <script type="text/javascript">
44130  */
44131 /*
44132  * These classes are private internal classes
44133  */
44134 Roo.bootstrap.layout.Center = function(config){
44135     config.region = "center";
44136     Roo.bootstrap.layout.Region.call(this, config);
44137     this.visible = true;
44138     this.minWidth = config.minWidth || 20;
44139     this.minHeight = config.minHeight || 20;
44140 };
44141
44142 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44143     hide : function(){
44144         // center panel can't be hidden
44145     },
44146     
44147     show : function(){
44148         // center panel can't be hidden
44149     },
44150     
44151     getMinWidth: function(){
44152         return this.minWidth;
44153     },
44154     
44155     getMinHeight: function(){
44156         return this.minHeight;
44157     }
44158 });
44159
44160
44161
44162
44163  
44164
44165
44166
44167
44168
44169
44170 Roo.bootstrap.layout.North = function(config)
44171 {
44172     config.region = 'north';
44173     config.cursor = 'n-resize';
44174     
44175     Roo.bootstrap.layout.Split.call(this, config);
44176     
44177     
44178     if(this.split){
44179         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44180         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44181         this.split.el.addClass("roo-layout-split-v");
44182     }
44183     //var size = config.initialSize || config.height;
44184     //if(this.el && typeof size != "undefined"){
44185     //    this.el.setHeight(size);
44186     //}
44187 };
44188 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44189 {
44190     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44191      
44192      
44193     onRender : function(ctr, pos)
44194     {
44195         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44196         var size = this.config.initialSize || this.config.height;
44197         if(this.el && typeof size != "undefined"){
44198             this.el.setHeight(size);
44199         }
44200     
44201     },
44202     
44203     getBox : function(){
44204         if(this.collapsed){
44205             return this.collapsedEl.getBox();
44206         }
44207         var box = this.el.getBox();
44208         if(this.split){
44209             box.height += this.split.el.getHeight();
44210         }
44211         return box;
44212     },
44213     
44214     updateBox : function(box){
44215         if(this.split && !this.collapsed){
44216             box.height -= this.split.el.getHeight();
44217             this.split.el.setLeft(box.x);
44218             this.split.el.setTop(box.y+box.height);
44219             this.split.el.setWidth(box.width);
44220         }
44221         if(this.collapsed){
44222             this.updateBody(box.width, null);
44223         }
44224         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44225     }
44226 });
44227
44228
44229
44230
44231
44232 Roo.bootstrap.layout.South = function(config){
44233     config.region = 'south';
44234     config.cursor = 's-resize';
44235     Roo.bootstrap.layout.Split.call(this, config);
44236     if(this.split){
44237         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44238         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44239         this.split.el.addClass("roo-layout-split-v");
44240     }
44241     
44242 };
44243
44244 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44245     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44246     
44247     onRender : function(ctr, pos)
44248     {
44249         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44250         var size = this.config.initialSize || this.config.height;
44251         if(this.el && typeof size != "undefined"){
44252             this.el.setHeight(size);
44253         }
44254     
44255     },
44256     
44257     getBox : function(){
44258         if(this.collapsed){
44259             return this.collapsedEl.getBox();
44260         }
44261         var box = this.el.getBox();
44262         if(this.split){
44263             var sh = this.split.el.getHeight();
44264             box.height += sh;
44265             box.y -= sh;
44266         }
44267         return box;
44268     },
44269     
44270     updateBox : function(box){
44271         if(this.split && !this.collapsed){
44272             var sh = this.split.el.getHeight();
44273             box.height -= sh;
44274             box.y += sh;
44275             this.split.el.setLeft(box.x);
44276             this.split.el.setTop(box.y-sh);
44277             this.split.el.setWidth(box.width);
44278         }
44279         if(this.collapsed){
44280             this.updateBody(box.width, null);
44281         }
44282         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44283     }
44284 });
44285
44286 Roo.bootstrap.layout.East = function(config){
44287     config.region = "east";
44288     config.cursor = "e-resize";
44289     Roo.bootstrap.layout.Split.call(this, config);
44290     if(this.split){
44291         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44292         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44293         this.split.el.addClass("roo-layout-split-h");
44294     }
44295     
44296 };
44297 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44298     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44299     
44300     onRender : function(ctr, pos)
44301     {
44302         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44303         var size = this.config.initialSize || this.config.width;
44304         if(this.el && typeof size != "undefined"){
44305             this.el.setWidth(size);
44306         }
44307     
44308     },
44309     
44310     getBox : function(){
44311         if(this.collapsed){
44312             return this.collapsedEl.getBox();
44313         }
44314         var box = this.el.getBox();
44315         if(this.split){
44316             var sw = this.split.el.getWidth();
44317             box.width += sw;
44318             box.x -= sw;
44319         }
44320         return box;
44321     },
44322
44323     updateBox : function(box){
44324         if(this.split && !this.collapsed){
44325             var sw = this.split.el.getWidth();
44326             box.width -= sw;
44327             this.split.el.setLeft(box.x);
44328             this.split.el.setTop(box.y);
44329             this.split.el.setHeight(box.height);
44330             box.x += sw;
44331         }
44332         if(this.collapsed){
44333             this.updateBody(null, box.height);
44334         }
44335         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44336     }
44337 });
44338
44339 Roo.bootstrap.layout.West = function(config){
44340     config.region = "west";
44341     config.cursor = "w-resize";
44342     
44343     Roo.bootstrap.layout.Split.call(this, config);
44344     if(this.split){
44345         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44346         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44347         this.split.el.addClass("roo-layout-split-h");
44348     }
44349     
44350 };
44351 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44352     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44353     
44354     onRender: function(ctr, pos)
44355     {
44356         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44357         var size = this.config.initialSize || this.config.width;
44358         if(typeof size != "undefined"){
44359             this.el.setWidth(size);
44360         }
44361     },
44362     
44363     getBox : function(){
44364         if(this.collapsed){
44365             return this.collapsedEl.getBox();
44366         }
44367         var box = this.el.getBox();
44368         if (box.width == 0) {
44369             box.width = this.config.width; // kludge?
44370         }
44371         if(this.split){
44372             box.width += this.split.el.getWidth();
44373         }
44374         return box;
44375     },
44376     
44377     updateBox : function(box){
44378         if(this.split && !this.collapsed){
44379             var sw = this.split.el.getWidth();
44380             box.width -= sw;
44381             this.split.el.setLeft(box.x+box.width);
44382             this.split.el.setTop(box.y);
44383             this.split.el.setHeight(box.height);
44384         }
44385         if(this.collapsed){
44386             this.updateBody(null, box.height);
44387         }
44388         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44389     }
44390 });/*
44391  * Based on:
44392  * Ext JS Library 1.1.1
44393  * Copyright(c) 2006-2007, Ext JS, LLC.
44394  *
44395  * Originally Released Under LGPL - original licence link has changed is not relivant.
44396  *
44397  * Fork - LGPL
44398  * <script type="text/javascript">
44399  */
44400 /**
44401  * @class Roo.bootstrap.paenl.Content
44402  * @extends Roo.util.Observable
44403  * @children Roo.bootstrap.Component
44404  * @parent builder Roo.bootstrap.layout.Border
44405  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44406  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44407  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44408  * @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
44409  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44410  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44411  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44412  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44413  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44414  * @cfg {String} title          The title for this panel
44415  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44416  * @cfg {String} url            Calls {@link #setUrl} with this value
44417  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44418  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44419  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44420  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44421  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44422  * @cfg {Boolean} badges render the badges
44423  * @cfg {String} cls  extra classes to use  
44424  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44425  
44426  * @constructor
44427  * Create a new ContentPanel.
44428  * @param {String/Object} config A string to set only the title or a config object
44429  
44430  */
44431 Roo.bootstrap.panel.Content = function( config){
44432     
44433     this.tpl = config.tpl || false;
44434     
44435     var el = config.el;
44436     var content = config.content;
44437
44438     if(config.autoCreate){ // xtype is available if this is called from factory
44439         el = Roo.id();
44440     }
44441     this.el = Roo.get(el);
44442     if(!this.el && config && config.autoCreate){
44443         if(typeof config.autoCreate == "object"){
44444             if(!config.autoCreate.id){
44445                 config.autoCreate.id = config.id||el;
44446             }
44447             this.el = Roo.DomHelper.append(document.body,
44448                         config.autoCreate, true);
44449         }else{
44450             var elcfg =  {
44451                 tag: "div",
44452                 cls: (config.cls || '') +
44453                     (config.background ? ' bg-' + config.background : '') +
44454                     " roo-layout-inactive-content",
44455                 id: config.id||el
44456             };
44457             if (config.iframe) {
44458                 elcfg.cn = [
44459                     {
44460                         tag : 'iframe',
44461                         style : 'border: 0px',
44462                         src : 'about:blank'
44463                     }
44464                 ];
44465             }
44466               
44467             if (config.html) {
44468                 elcfg.html = config.html;
44469                 
44470             }
44471                         
44472             this.el = Roo.DomHelper.append(document.body, elcfg , true);
44473             if (config.iframe) {
44474                 this.iframeEl = this.el.select('iframe',true).first();
44475             }
44476             
44477         }
44478     } 
44479     this.closable = false;
44480     this.loaded = false;
44481     this.active = false;
44482    
44483       
44484     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44485         
44486         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44487         
44488         this.wrapEl = this.el; //this.el.wrap();
44489         var ti = [];
44490         if (config.toolbar.items) {
44491             ti = config.toolbar.items ;
44492             delete config.toolbar.items ;
44493         }
44494         
44495         var nitems = [];
44496         this.toolbar.render(this.wrapEl, 'before');
44497         for(var i =0;i < ti.length;i++) {
44498           //  Roo.log(['add child', items[i]]);
44499             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44500         }
44501         this.toolbar.items = nitems;
44502         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44503         delete config.toolbar;
44504         
44505     }
44506     /*
44507     // xtype created footer. - not sure if will work as we normally have to render first..
44508     if (this.footer && !this.footer.el && this.footer.xtype) {
44509         if (!this.wrapEl) {
44510             this.wrapEl = this.el.wrap();
44511         }
44512     
44513         this.footer.container = this.wrapEl.createChild();
44514          
44515         this.footer = Roo.factory(this.footer, Roo);
44516         
44517     }
44518     */
44519     
44520      if(typeof config == "string"){
44521         this.title = config;
44522     }else{
44523         Roo.apply(this, config);
44524     }
44525     
44526     if(this.resizeEl){
44527         this.resizeEl = Roo.get(this.resizeEl, true);
44528     }else{
44529         this.resizeEl = this.el;
44530     }
44531     // handle view.xtype
44532     
44533  
44534     
44535     
44536     this.addEvents({
44537         /**
44538          * @event activate
44539          * Fires when this panel is activated. 
44540          * @param {Roo.ContentPanel} this
44541          */
44542         "activate" : true,
44543         /**
44544          * @event deactivate
44545          * Fires when this panel is activated. 
44546          * @param {Roo.ContentPanel} this
44547          */
44548         "deactivate" : true,
44549
44550         /**
44551          * @event resize
44552          * Fires when this panel is resized if fitToFrame is true.
44553          * @param {Roo.ContentPanel} this
44554          * @param {Number} width The width after any component adjustments
44555          * @param {Number} height The height after any component adjustments
44556          */
44557         "resize" : true,
44558         
44559          /**
44560          * @event render
44561          * Fires when this tab is created
44562          * @param {Roo.ContentPanel} this
44563          */
44564         "render" : true,
44565         
44566           /**
44567          * @event scroll
44568          * Fires when this content is scrolled
44569          * @param {Roo.ContentPanel} this
44570          * @param {Event} scrollEvent
44571          */
44572         "scroll" : true
44573         
44574         
44575         
44576     });
44577     
44578
44579     
44580     
44581     if(this.autoScroll && !this.iframe){
44582         this.resizeEl.setStyle("overflow", "auto");
44583         this.resizeEl.on('scroll', this.onScroll, this);
44584     } else {
44585         // fix randome scrolling
44586         //this.el.on('scroll', function() {
44587         //    Roo.log('fix random scolling');
44588         //    this.scrollTo('top',0); 
44589         //});
44590     }
44591     content = content || this.content;
44592     if(content){
44593         this.setContent(content);
44594     }
44595     if(config && config.url){
44596         this.setUrl(this.url, this.params, this.loadOnce);
44597     }
44598     
44599     
44600     
44601     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
44602     
44603     if (this.view && typeof(this.view.xtype) != 'undefined') {
44604         this.view.el = this.el.appendChild(document.createElement("div"));
44605         this.view = Roo.factory(this.view); 
44606         this.view.render  &&  this.view.render(false, '');  
44607     }
44608     
44609     
44610     this.fireEvent('render', this);
44611 };
44612
44613 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
44614     
44615     cls : '',
44616     background : '',
44617     
44618     tabTip : '',
44619     
44620     iframe : false,
44621     iframeEl : false,
44622     
44623     /* Resize Element - use this to work out scroll etc. */
44624     resizeEl : false,
44625     
44626     setRegion : function(region){
44627         this.region = region;
44628         this.setActiveClass(region && !this.background);
44629     },
44630     
44631     
44632     setActiveClass: function(state)
44633     {
44634         if(state){
44635            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
44636            this.el.setStyle('position','relative');
44637         }else{
44638            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
44639            this.el.setStyle('position', 'absolute');
44640         } 
44641     },
44642     
44643     /**
44644      * Returns the toolbar for this Panel if one was configured. 
44645      * @return {Roo.Toolbar} 
44646      */
44647     getToolbar : function(){
44648         return this.toolbar;
44649     },
44650     
44651     setActiveState : function(active)
44652     {
44653         this.active = active;
44654         this.setActiveClass(active);
44655         if(!active){
44656             if(this.fireEvent("deactivate", this) === false){
44657                 return false;
44658             }
44659             return true;
44660         }
44661         this.fireEvent("activate", this);
44662         return true;
44663     },
44664     /**
44665      * Updates this panel's element (not for iframe)
44666      * @param {String} content The new content
44667      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44668     */
44669     setContent : function(content, loadScripts){
44670         if (this.iframe) {
44671             return;
44672         }
44673         
44674         this.el.update(content, loadScripts);
44675     },
44676
44677     ignoreResize : function(w, h)
44678     {
44679         //return false; // always resize?
44680         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44681             return true;
44682         }else{
44683             this.lastSize = {width: w, height: h};
44684             return false;
44685         }
44686     },
44687     /**
44688      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44689      * @return {Roo.UpdateManager} The UpdateManager
44690      */
44691     getUpdateManager : function(){
44692         if (this.iframe) {
44693             return false;
44694         }
44695         return this.el.getUpdateManager();
44696     },
44697      /**
44698      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44699      * Does not work with IFRAME contents
44700      * @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:
44701 <pre><code>
44702 panel.load({
44703     url: "your-url.php",
44704     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44705     callback: yourFunction,
44706     scope: yourObject, //(optional scope)
44707     discardUrl: false,
44708     nocache: false,
44709     text: "Loading...",
44710     timeout: 30,
44711     scripts: false
44712 });
44713 </code></pre>
44714      
44715      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44716      * 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.
44717      * @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}
44718      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44719      * @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.
44720      * @return {Roo.ContentPanel} this
44721      */
44722     load : function(){
44723         
44724         if (this.iframe) {
44725             return this;
44726         }
44727         
44728         var um = this.el.getUpdateManager();
44729         um.update.apply(um, arguments);
44730         return this;
44731     },
44732
44733
44734     /**
44735      * 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.
44736      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44737      * @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)
44738      * @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)
44739      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
44740      */
44741     setUrl : function(url, params, loadOnce){
44742         if (this.iframe) {
44743             this.iframeEl.dom.src = url;
44744             return false;
44745         }
44746         
44747         if(this.refreshDelegate){
44748             this.removeListener("activate", this.refreshDelegate);
44749         }
44750         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44751         this.on("activate", this.refreshDelegate);
44752         return this.el.getUpdateManager();
44753     },
44754     
44755     _handleRefresh : function(url, params, loadOnce){
44756         if(!loadOnce || !this.loaded){
44757             var updater = this.el.getUpdateManager();
44758             updater.update(url, params, this._setLoaded.createDelegate(this));
44759         }
44760     },
44761     
44762     _setLoaded : function(){
44763         this.loaded = true;
44764     }, 
44765     
44766     /**
44767      * Returns this panel's id
44768      * @return {String} 
44769      */
44770     getId : function(){
44771         return this.el.id;
44772     },
44773     
44774     /** 
44775      * Returns this panel's element - used by regiosn to add.
44776      * @return {Roo.Element} 
44777      */
44778     getEl : function(){
44779         return this.wrapEl || this.el;
44780     },
44781     
44782    
44783     
44784     adjustForComponents : function(width, height)
44785     {
44786         //Roo.log('adjustForComponents ');
44787         if(this.resizeEl != this.el){
44788             width -= this.el.getFrameWidth('lr');
44789             height -= this.el.getFrameWidth('tb');
44790         }
44791         if(this.toolbar){
44792             var te = this.toolbar.getEl();
44793             te.setWidth(width);
44794             height -= te.getHeight();
44795         }
44796         if(this.footer){
44797             var te = this.footer.getEl();
44798             te.setWidth(width);
44799             height -= te.getHeight();
44800         }
44801         
44802         
44803         if(this.adjustments){
44804             width += this.adjustments[0];
44805             height += this.adjustments[1];
44806         }
44807         return {"width": width, "height": height};
44808     },
44809     
44810     setSize : function(width, height){
44811         if(this.fitToFrame && !this.ignoreResize(width, height)){
44812             if(this.fitContainer && this.resizeEl != this.el){
44813                 this.el.setSize(width, height);
44814             }
44815             var size = this.adjustForComponents(width, height);
44816             if (this.iframe) {
44817                 this.iframeEl.setSize(width,height);
44818             }
44819             
44820             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44821             this.fireEvent('resize', this, size.width, size.height);
44822             
44823             
44824         }
44825     },
44826     
44827     /**
44828      * Returns this panel's title
44829      * @return {String} 
44830      */
44831     getTitle : function(){
44832         
44833         if (typeof(this.title) != 'object') {
44834             return this.title;
44835         }
44836         
44837         var t = '';
44838         for (var k in this.title) {
44839             if (!this.title.hasOwnProperty(k)) {
44840                 continue;
44841             }
44842             
44843             if (k.indexOf('-') >= 0) {
44844                 var s = k.split('-');
44845                 for (var i = 0; i<s.length; i++) {
44846                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
44847                 }
44848             } else {
44849                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
44850             }
44851         }
44852         return t;
44853     },
44854     
44855     /**
44856      * Set this panel's title
44857      * @param {String} title
44858      */
44859     setTitle : function(title){
44860         this.title = title;
44861         if(this.region){
44862             this.region.updatePanelTitle(this, title);
44863         }
44864     },
44865     
44866     /**
44867      * Returns true is this panel was configured to be closable
44868      * @return {Boolean} 
44869      */
44870     isClosable : function(){
44871         return this.closable;
44872     },
44873     
44874     beforeSlide : function(){
44875         this.el.clip();
44876         this.resizeEl.clip();
44877     },
44878     
44879     afterSlide : function(){
44880         this.el.unclip();
44881         this.resizeEl.unclip();
44882     },
44883     
44884     /**
44885      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44886      *   Will fail silently if the {@link #setUrl} method has not been called.
44887      *   This does not activate the panel, just updates its content.
44888      */
44889     refresh : function(){
44890         if(this.refreshDelegate){
44891            this.loaded = false;
44892            this.refreshDelegate();
44893         }
44894     },
44895     
44896     /**
44897      * Destroys this panel
44898      */
44899     destroy : function(){
44900         this.el.removeAllListeners();
44901         var tempEl = document.createElement("span");
44902         tempEl.appendChild(this.el.dom);
44903         tempEl.innerHTML = "";
44904         this.el.remove();
44905         this.el = null;
44906     },
44907     
44908     /**
44909      * form - if the content panel contains a form - this is a reference to it.
44910      * @type {Roo.form.Form}
44911      */
44912     form : false,
44913     /**
44914      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
44915      *    This contains a reference to it.
44916      * @type {Roo.View}
44917      */
44918     view : false,
44919     
44920       /**
44921      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44922      * <pre><code>
44923
44924 layout.addxtype({
44925        xtype : 'Form',
44926        items: [ .... ]
44927    }
44928 );
44929
44930 </code></pre>
44931      * @param {Object} cfg Xtype definition of item to add.
44932      */
44933     
44934     
44935     getChildContainer: function () {
44936         return this.getEl();
44937     },
44938     
44939     
44940     onScroll : function(e)
44941     {
44942         this.fireEvent('scroll', this, e);
44943     }
44944     
44945     
44946     /*
44947         var  ret = new Roo.factory(cfg);
44948         return ret;
44949         
44950         
44951         // add form..
44952         if (cfg.xtype.match(/^Form$/)) {
44953             
44954             var el;
44955             //if (this.footer) {
44956             //    el = this.footer.container.insertSibling(false, 'before');
44957             //} else {
44958                 el = this.el.createChild();
44959             //}
44960
44961             this.form = new  Roo.form.Form(cfg);
44962             
44963             
44964             if ( this.form.allItems.length) {
44965                 this.form.render(el.dom);
44966             }
44967             return this.form;
44968         }
44969         // should only have one of theses..
44970         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
44971             // views.. should not be just added - used named prop 'view''
44972             
44973             cfg.el = this.el.appendChild(document.createElement("div"));
44974             // factory?
44975             
44976             var ret = new Roo.factory(cfg);
44977              
44978              ret.render && ret.render(false, ''); // render blank..
44979             this.view = ret;
44980             return ret;
44981         }
44982         return false;
44983     }
44984     \*/
44985 });
44986  
44987 /**
44988  * @class Roo.bootstrap.panel.Grid
44989  * @extends Roo.bootstrap.panel.Content
44990  * @constructor
44991  * Create a new GridPanel.
44992  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
44993  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
44994  * @param {Object} config A the config object
44995   
44996  */
44997
44998
44999
45000 Roo.bootstrap.panel.Grid = function(config)
45001 {
45002     
45003       
45004     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45005         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45006
45007     config.el = this.wrapper;
45008     //this.el = this.wrapper;
45009     
45010       if (config.container) {
45011         // ctor'ed from a Border/panel.grid
45012         
45013         
45014         this.wrapper.setStyle("overflow", "hidden");
45015         this.wrapper.addClass('roo-grid-container');
45016
45017     }
45018     
45019     
45020     if(config.toolbar){
45021         var tool_el = this.wrapper.createChild();    
45022         this.toolbar = Roo.factory(config.toolbar);
45023         var ti = [];
45024         if (config.toolbar.items) {
45025             ti = config.toolbar.items ;
45026             delete config.toolbar.items ;
45027         }
45028         
45029         var nitems = [];
45030         this.toolbar.render(tool_el);
45031         for(var i =0;i < ti.length;i++) {
45032           //  Roo.log(['add child', items[i]]);
45033             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45034         }
45035         this.toolbar.items = nitems;
45036         
45037         delete config.toolbar;
45038     }
45039     
45040     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45041     config.grid.scrollBody = true;;
45042     config.grid.monitorWindowResize = false; // turn off autosizing
45043     config.grid.autoHeight = false;
45044     config.grid.autoWidth = false;
45045     
45046     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45047     
45048     if (config.background) {
45049         // render grid on panel activation (if panel background)
45050         this.on('activate', function(gp) {
45051             if (!gp.grid.rendered) {
45052                 gp.grid.render(this.wrapper);
45053                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45054             }
45055         });
45056             
45057     } else {
45058         this.grid.render(this.wrapper);
45059         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45060
45061     }
45062     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45063     // ??? needed ??? config.el = this.wrapper;
45064     
45065     
45066     
45067   
45068     // xtype created footer. - not sure if will work as we normally have to render first..
45069     if (this.footer && !this.footer.el && this.footer.xtype) {
45070         
45071         var ctr = this.grid.getView().getFooterPanel(true);
45072         this.footer.dataSource = this.grid.dataSource;
45073         this.footer = Roo.factory(this.footer, Roo);
45074         this.footer.render(ctr);
45075         
45076     }
45077     
45078     
45079     
45080     
45081      
45082 };
45083
45084 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45085 {
45086   
45087     getId : function(){
45088         return this.grid.id;
45089     },
45090     
45091     /**
45092      * Returns the grid for this panel
45093      * @return {Roo.bootstrap.Table} 
45094      */
45095     getGrid : function(){
45096         return this.grid;    
45097     },
45098     
45099     setSize : function(width, height)
45100     {
45101      
45102         //if(!this.ignoreResize(width, height)){
45103             var grid = this.grid;
45104             var size = this.adjustForComponents(width, height);
45105             // tfoot is not a footer?
45106           
45107             
45108             var gridel = grid.getGridEl();
45109             gridel.setSize(size.width, size.height);
45110             
45111             var tbd = grid.getGridEl().select('tbody', true).first();
45112             var thd = grid.getGridEl().select('thead',true).first();
45113             var tbf= grid.getGridEl().select('tfoot', true).first();
45114
45115             if (tbf) {
45116                 size.height -= tbf.getHeight();
45117             }
45118             if (thd) {
45119                 size.height -= thd.getHeight();
45120             }
45121             
45122             tbd.setSize(size.width, size.height );
45123             // this is for the account management tab -seems to work there.
45124             var thd = grid.getGridEl().select('thead',true).first();
45125             //if (tbd) {
45126             //    tbd.setSize(size.width, size.height - thd.getHeight());
45127             //}
45128              
45129             grid.autoSize();
45130         //}
45131    
45132     },
45133      
45134     
45135     
45136     beforeSlide : function(){
45137         this.grid.getView().scroller.clip();
45138     },
45139     
45140     afterSlide : function(){
45141         this.grid.getView().scroller.unclip();
45142     },
45143     
45144     destroy : function(){
45145         this.grid.destroy();
45146         delete this.grid;
45147         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45148     }
45149 });
45150
45151 /**
45152  * @class Roo.bootstrap.panel.Nest
45153  * @extends Roo.bootstrap.panel.Content
45154  * @constructor
45155  * Create a new Panel, that can contain a layout.Border.
45156  * 
45157  * 
45158  * @param {String/Object} config A string to set only the title or a config object
45159  */
45160 Roo.bootstrap.panel.Nest = function(config)
45161 {
45162     // construct with only one argument..
45163     /* FIXME - implement nicer consturctors
45164     if (layout.layout) {
45165         config = layout;
45166         layout = config.layout;
45167         delete config.layout;
45168     }
45169     if (layout.xtype && !layout.getEl) {
45170         // then layout needs constructing..
45171         layout = Roo.factory(layout, Roo);
45172     }
45173     */
45174     
45175     config.el =  config.layout.getEl();
45176     
45177     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45178     
45179     config.layout.monitorWindowResize = false; // turn off autosizing
45180     this.layout = config.layout;
45181     this.layout.getEl().addClass("roo-layout-nested-layout");
45182     this.layout.parent = this;
45183     
45184     
45185     
45186     
45187 };
45188
45189 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45190     /**
45191     * @cfg {Roo.BorderLayout} layout The layout for this panel
45192     */
45193     layout : false,
45194
45195     setSize : function(width, height){
45196         if(!this.ignoreResize(width, height)){
45197             var size = this.adjustForComponents(width, height);
45198             var el = this.layout.getEl();
45199             if (size.height < 1) {
45200                 el.setWidth(size.width);   
45201             } else {
45202                 el.setSize(size.width, size.height);
45203             }
45204             var touch = el.dom.offsetWidth;
45205             this.layout.layout();
45206             // ie requires a double layout on the first pass
45207             if(Roo.isIE && !this.initialized){
45208                 this.initialized = true;
45209                 this.layout.layout();
45210             }
45211         }
45212     },
45213     
45214     // activate all subpanels if not currently active..
45215     
45216     setActiveState : function(active){
45217         this.active = active;
45218         this.setActiveClass(active);
45219         
45220         if(!active){
45221             this.fireEvent("deactivate", this);
45222             return;
45223         }
45224         
45225         this.fireEvent("activate", this);
45226         // not sure if this should happen before or after..
45227         if (!this.layout) {
45228             return; // should not happen..
45229         }
45230         var reg = false;
45231         for (var r in this.layout.regions) {
45232             reg = this.layout.getRegion(r);
45233             if (reg.getActivePanel()) {
45234                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45235                 reg.setActivePanel(reg.getActivePanel());
45236                 continue;
45237             }
45238             if (!reg.panels.length) {
45239                 continue;
45240             }
45241             reg.showPanel(reg.getPanel(0));
45242         }
45243         
45244         
45245         
45246         
45247     },
45248     
45249     /**
45250      * Returns the nested BorderLayout for this panel
45251      * @return {Roo.BorderLayout} 
45252      */
45253     getLayout : function(){
45254         return this.layout;
45255     },
45256     
45257      /**
45258      * Adds a xtype elements to the layout of the nested panel
45259      * <pre><code>
45260
45261 panel.addxtype({
45262        xtype : 'ContentPanel',
45263        region: 'west',
45264        items: [ .... ]
45265    }
45266 );
45267
45268 panel.addxtype({
45269         xtype : 'NestedLayoutPanel',
45270         region: 'west',
45271         layout: {
45272            center: { },
45273            west: { }   
45274         },
45275         items : [ ... list of content panels or nested layout panels.. ]
45276    }
45277 );
45278 </code></pre>
45279      * @param {Object} cfg Xtype definition of item to add.
45280      */
45281     addxtype : function(cfg) {
45282         return this.layout.addxtype(cfg);
45283     
45284     }
45285 });/*
45286  * Based on:
45287  * Ext JS Library 1.1.1
45288  * Copyright(c) 2006-2007, Ext JS, LLC.
45289  *
45290  * Originally Released Under LGPL - original licence link has changed is not relivant.
45291  *
45292  * Fork - LGPL
45293  * <script type="text/javascript">
45294  */
45295 /**
45296  * @class Roo.TabPanel
45297  * @extends Roo.util.Observable
45298  * A lightweight tab container.
45299  * <br><br>
45300  * Usage:
45301  * <pre><code>
45302 // basic tabs 1, built from existing content
45303 var tabs = new Roo.TabPanel("tabs1");
45304 tabs.addTab("script", "View Script");
45305 tabs.addTab("markup", "View Markup");
45306 tabs.activate("script");
45307
45308 // more advanced tabs, built from javascript
45309 var jtabs = new Roo.TabPanel("jtabs");
45310 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45311
45312 // set up the UpdateManager
45313 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45314 var updater = tab2.getUpdateManager();
45315 updater.setDefaultUrl("ajax1.htm");
45316 tab2.on('activate', updater.refresh, updater, true);
45317
45318 // Use setUrl for Ajax loading
45319 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45320 tab3.setUrl("ajax2.htm", null, true);
45321
45322 // Disabled tab
45323 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45324 tab4.disable();
45325
45326 jtabs.activate("jtabs-1");
45327  * </code></pre>
45328  * @constructor
45329  * Create a new TabPanel.
45330  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45331  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45332  */
45333 Roo.bootstrap.panel.Tabs = function(config){
45334     /**
45335     * The container element for this TabPanel.
45336     * @type Roo.Element
45337     */
45338     this.el = Roo.get(config.el);
45339     delete config.el;
45340     if(config){
45341         if(typeof config == "boolean"){
45342             this.tabPosition = config ? "bottom" : "top";
45343         }else{
45344             Roo.apply(this, config);
45345         }
45346     }
45347     
45348     if(this.tabPosition == "bottom"){
45349         // if tabs are at the bottom = create the body first.
45350         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45351         this.el.addClass("roo-tabs-bottom");
45352     }
45353     // next create the tabs holders
45354     
45355     if (this.tabPosition == "west"){
45356         
45357         var reg = this.region; // fake it..
45358         while (reg) {
45359             if (!reg.mgr.parent) {
45360                 break;
45361             }
45362             reg = reg.mgr.parent.region;
45363         }
45364         Roo.log("got nest?");
45365         Roo.log(reg);
45366         if (reg.mgr.getRegion('west')) {
45367             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45368             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45369             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45370             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45371             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45372         
45373             
45374         }
45375         
45376         
45377     } else {
45378      
45379         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45380         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45381         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45382         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45383     }
45384     
45385     
45386     if(Roo.isIE){
45387         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45388     }
45389     
45390     // finally - if tabs are at the top, then create the body last..
45391     if(this.tabPosition != "bottom"){
45392         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45393          * @type Roo.Element
45394          */
45395         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45396         this.el.addClass("roo-tabs-top");
45397     }
45398     this.items = [];
45399
45400     this.bodyEl.setStyle("position", "relative");
45401
45402     this.active = null;
45403     this.activateDelegate = this.activate.createDelegate(this);
45404
45405     this.addEvents({
45406         /**
45407          * @event tabchange
45408          * Fires when the active tab changes
45409          * @param {Roo.TabPanel} this
45410          * @param {Roo.TabPanelItem} activePanel The new active tab
45411          */
45412         "tabchange": true,
45413         /**
45414          * @event beforetabchange
45415          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45416          * @param {Roo.TabPanel} this
45417          * @param {Object} e Set cancel to true on this object to cancel the tab change
45418          * @param {Roo.TabPanelItem} tab The tab being changed to
45419          */
45420         "beforetabchange" : true
45421     });
45422
45423     Roo.EventManager.onWindowResize(this.onResize, this);
45424     this.cpad = this.el.getPadding("lr");
45425     this.hiddenCount = 0;
45426
45427
45428     // toolbar on the tabbar support...
45429     if (this.toolbar) {
45430         alert("no toolbar support yet");
45431         this.toolbar  = false;
45432         /*
45433         var tcfg = this.toolbar;
45434         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
45435         this.toolbar = new Roo.Toolbar(tcfg);
45436         if (Roo.isSafari) {
45437             var tbl = tcfg.container.child('table', true);
45438             tbl.setAttribute('width', '100%');
45439         }
45440         */
45441         
45442     }
45443    
45444
45445
45446     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45447 };
45448
45449 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45450     /*
45451      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45452      */
45453     tabPosition : "top",
45454     /*
45455      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45456      */
45457     currentTabWidth : 0,
45458     /*
45459      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45460      */
45461     minTabWidth : 40,
45462     /*
45463      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45464      */
45465     maxTabWidth : 250,
45466     /*
45467      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45468      */
45469     preferredTabWidth : 175,
45470     /*
45471      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45472      */
45473     resizeTabs : false,
45474     /*
45475      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45476      */
45477     monitorResize : true,
45478     /*
45479      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
45480      */
45481     toolbar : false,  // set by caller..
45482     
45483     region : false, /// set by caller
45484     
45485     disableTooltips : true, // not used yet...
45486
45487     /**
45488      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45489      * @param {String} id The id of the div to use <b>or create</b>
45490      * @param {String} text The text for the tab
45491      * @param {String} content (optional) Content to put in the TabPanelItem body
45492      * @param {Boolean} closable (optional) True to create a close icon on the tab
45493      * @return {Roo.TabPanelItem} The created TabPanelItem
45494      */
45495     addTab : function(id, text, content, closable, tpl)
45496     {
45497         var item = new Roo.bootstrap.panel.TabItem({
45498             panel: this,
45499             id : id,
45500             text : text,
45501             closable : closable,
45502             tpl : tpl
45503         });
45504         this.addTabItem(item);
45505         if(content){
45506             item.setContent(content);
45507         }
45508         return item;
45509     },
45510
45511     /**
45512      * Returns the {@link Roo.TabPanelItem} with the specified id/index
45513      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45514      * @return {Roo.TabPanelItem}
45515      */
45516     getTab : function(id){
45517         return this.items[id];
45518     },
45519
45520     /**
45521      * Hides the {@link Roo.TabPanelItem} with the specified id/index
45522      * @param {String/Number} id The id or index of the TabPanelItem to hide.
45523      */
45524     hideTab : function(id){
45525         var t = this.items[id];
45526         if(!t.isHidden()){
45527            t.setHidden(true);
45528            this.hiddenCount++;
45529            this.autoSizeTabs();
45530         }
45531     },
45532
45533     /**
45534      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
45535      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
45536      */
45537     unhideTab : function(id){
45538         var t = this.items[id];
45539         if(t.isHidden()){
45540            t.setHidden(false);
45541            this.hiddenCount--;
45542            this.autoSizeTabs();
45543         }
45544     },
45545
45546     /**
45547      * Adds an existing {@link Roo.TabPanelItem}.
45548      * @param {Roo.TabPanelItem} item The TabPanelItem to add
45549      */
45550     addTabItem : function(item)
45551     {
45552         this.items[item.id] = item;
45553         this.items.push(item);
45554         this.autoSizeTabs();
45555       //  if(this.resizeTabs){
45556     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
45557   //         this.autoSizeTabs();
45558 //        }else{
45559 //            item.autoSize();
45560        // }
45561     },
45562
45563     /**
45564      * Removes a {@link Roo.TabPanelItem}.
45565      * @param {String/Number} id The id or index of the TabPanelItem to remove.
45566      */
45567     removeTab : function(id){
45568         var items = this.items;
45569         var tab = items[id];
45570         if(!tab) { return; }
45571         var index = items.indexOf(tab);
45572         if(this.active == tab && items.length > 1){
45573             var newTab = this.getNextAvailable(index);
45574             if(newTab) {
45575                 newTab.activate();
45576             }
45577         }
45578         this.stripEl.dom.removeChild(tab.pnode.dom);
45579         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
45580             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
45581         }
45582         items.splice(index, 1);
45583         delete this.items[tab.id];
45584         tab.fireEvent("close", tab);
45585         tab.purgeListeners();
45586         this.autoSizeTabs();
45587     },
45588
45589     getNextAvailable : function(start){
45590         var items = this.items;
45591         var index = start;
45592         // look for a next tab that will slide over to
45593         // replace the one being removed
45594         while(index < items.length){
45595             var item = items[++index];
45596             if(item && !item.isHidden()){
45597                 return item;
45598             }
45599         }
45600         // if one isn't found select the previous tab (on the left)
45601         index = start;
45602         while(index >= 0){
45603             var item = items[--index];
45604             if(item && !item.isHidden()){
45605                 return item;
45606             }
45607         }
45608         return null;
45609     },
45610
45611     /**
45612      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
45613      * @param {String/Number} id The id or index of the TabPanelItem to disable.
45614      */
45615     disableTab : function(id){
45616         var tab = this.items[id];
45617         if(tab && this.active != tab){
45618             tab.disable();
45619         }
45620     },
45621
45622     /**
45623      * Enables a {@link Roo.TabPanelItem} that is disabled.
45624      * @param {String/Number} id The id or index of the TabPanelItem to enable.
45625      */
45626     enableTab : function(id){
45627         var tab = this.items[id];
45628         tab.enable();
45629     },
45630
45631     /**
45632      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
45633      * @param {String/Number} id The id or index of the TabPanelItem to activate.
45634      * @return {Roo.TabPanelItem} The TabPanelItem.
45635      */
45636     activate : function(id)
45637     {
45638         //Roo.log('activite:'  + id);
45639         
45640         var tab = this.items[id];
45641         if(!tab){
45642             return null;
45643         }
45644         if(tab == this.active || tab.disabled){
45645             return tab;
45646         }
45647         var e = {};
45648         this.fireEvent("beforetabchange", this, e, tab);
45649         if(e.cancel !== true && !tab.disabled){
45650             if(this.active){
45651                 this.active.hide();
45652             }
45653             this.active = this.items[id];
45654             this.active.show();
45655             this.fireEvent("tabchange", this, this.active);
45656         }
45657         return tab;
45658     },
45659
45660     /**
45661      * Gets the active {@link Roo.TabPanelItem}.
45662      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
45663      */
45664     getActiveTab : function(){
45665         return this.active;
45666     },
45667
45668     /**
45669      * Updates the tab body element to fit the height of the container element
45670      * for overflow scrolling
45671      * @param {Number} targetHeight (optional) Override the starting height from the elements height
45672      */
45673     syncHeight : function(targetHeight){
45674         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45675         var bm = this.bodyEl.getMargins();
45676         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
45677         this.bodyEl.setHeight(newHeight);
45678         return newHeight;
45679     },
45680
45681     onResize : function(){
45682         if(this.monitorResize){
45683             this.autoSizeTabs();
45684         }
45685     },
45686
45687     /**
45688      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
45689      */
45690     beginUpdate : function(){
45691         this.updating = true;
45692     },
45693
45694     /**
45695      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
45696      */
45697     endUpdate : function(){
45698         this.updating = false;
45699         this.autoSizeTabs();
45700     },
45701
45702     /**
45703      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
45704      */
45705     autoSizeTabs : function()
45706     {
45707         var count = this.items.length;
45708         var vcount = count - this.hiddenCount;
45709         
45710         if (vcount < 2) {
45711             this.stripEl.hide();
45712         } else {
45713             this.stripEl.show();
45714         }
45715         
45716         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
45717             return;
45718         }
45719         
45720         
45721         var w = Math.max(this.el.getWidth() - this.cpad, 10);
45722         var availWidth = Math.floor(w / vcount);
45723         var b = this.stripBody;
45724         if(b.getWidth() > w){
45725             var tabs = this.items;
45726             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
45727             if(availWidth < this.minTabWidth){
45728                 /*if(!this.sleft){    // incomplete scrolling code
45729                     this.createScrollButtons();
45730                 }
45731                 this.showScroll();
45732                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
45733             }
45734         }else{
45735             if(this.currentTabWidth < this.preferredTabWidth){
45736                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
45737             }
45738         }
45739     },
45740
45741     /**
45742      * Returns the number of tabs in this TabPanel.
45743      * @return {Number}
45744      */
45745      getCount : function(){
45746          return this.items.length;
45747      },
45748
45749     /**
45750      * Resizes all the tabs to the passed width
45751      * @param {Number} The new width
45752      */
45753     setTabWidth : function(width){
45754         this.currentTabWidth = width;
45755         for(var i = 0, len = this.items.length; i < len; i++) {
45756                 if(!this.items[i].isHidden()) {
45757                 this.items[i].setWidth(width);
45758             }
45759         }
45760     },
45761
45762     /**
45763      * Destroys this TabPanel
45764      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
45765      */
45766     destroy : function(removeEl){
45767         Roo.EventManager.removeResizeListener(this.onResize, this);
45768         for(var i = 0, len = this.items.length; i < len; i++){
45769             this.items[i].purgeListeners();
45770         }
45771         if(removeEl === true){
45772             this.el.update("");
45773             this.el.remove();
45774         }
45775     },
45776     
45777     createStrip : function(container)
45778     {
45779         var strip = document.createElement("nav");
45780         strip.className = Roo.bootstrap.version == 4 ?
45781             "navbar-light bg-light" : 
45782             "navbar navbar-default"; //"x-tabs-wrap";
45783         container.appendChild(strip);
45784         return strip;
45785     },
45786     
45787     createStripList : function(strip)
45788     {
45789         // div wrapper for retard IE
45790         // returns the "tr" element.
45791         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
45792         //'<div class="x-tabs-strip-wrap">'+
45793           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
45794           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
45795         return strip.firstChild; //.firstChild.firstChild.firstChild;
45796     },
45797     createBody : function(container)
45798     {
45799         var body = document.createElement("div");
45800         Roo.id(body, "tab-body");
45801         //Roo.fly(body).addClass("x-tabs-body");
45802         Roo.fly(body).addClass("tab-content");
45803         container.appendChild(body);
45804         return body;
45805     },
45806     createItemBody :function(bodyEl, id){
45807         var body = Roo.getDom(id);
45808         if(!body){
45809             body = document.createElement("div");
45810             body.id = id;
45811         }
45812         //Roo.fly(body).addClass("x-tabs-item-body");
45813         Roo.fly(body).addClass("tab-pane");
45814          bodyEl.insertBefore(body, bodyEl.firstChild);
45815         return body;
45816     },
45817     /** @private */
45818     createStripElements :  function(stripEl, text, closable, tpl)
45819     {
45820         var td = document.createElement("li"); // was td..
45821         td.className = 'nav-item';
45822         
45823         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
45824         
45825         
45826         stripEl.appendChild(td);
45827         /*if(closable){
45828             td.className = "x-tabs-closable";
45829             if(!this.closeTpl){
45830                 this.closeTpl = new Roo.Template(
45831                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45832                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
45833                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
45834                 );
45835             }
45836             var el = this.closeTpl.overwrite(td, {"text": text});
45837             var close = el.getElementsByTagName("div")[0];
45838             var inner = el.getElementsByTagName("em")[0];
45839             return {"el": el, "close": close, "inner": inner};
45840         } else {
45841         */
45842         // not sure what this is..
45843 //            if(!this.tabTpl){
45844                 //this.tabTpl = new Roo.Template(
45845                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45846                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
45847                 //);
45848 //                this.tabTpl = new Roo.Template(
45849 //                   '<a href="#">' +
45850 //                   '<span unselectable="on"' +
45851 //                            (this.disableTooltips ? '' : ' title="{text}"') +
45852 //                            ' >{text}</span></a>'
45853 //                );
45854 //                
45855 //            }
45856
45857
45858             var template = tpl || this.tabTpl || false;
45859             
45860             if(!template){
45861                 template =  new Roo.Template(
45862                         Roo.bootstrap.version == 4 ? 
45863                             (
45864                                 '<a class="nav-link" href="#" unselectable="on"' +
45865                                      (this.disableTooltips ? '' : ' title="{text}"') +
45866                                      ' >{text}</a>'
45867                             ) : (
45868                                 '<a class="nav-link" href="#">' +
45869                                 '<span unselectable="on"' +
45870                                          (this.disableTooltips ? '' : ' title="{text}"') +
45871                                     ' >{text}</span></a>'
45872                             )
45873                 );
45874             }
45875             
45876             switch (typeof(template)) {
45877                 case 'object' :
45878                     break;
45879                 case 'string' :
45880                     template = new Roo.Template(template);
45881                     break;
45882                 default :
45883                     break;
45884             }
45885             
45886             var el = template.overwrite(td, {"text": text});
45887             
45888             var inner = el.getElementsByTagName("span")[0];
45889             
45890             return {"el": el, "inner": inner};
45891             
45892     }
45893         
45894     
45895 });
45896
45897 /**
45898  * @class Roo.TabPanelItem
45899  * @extends Roo.util.Observable
45900  * Represents an individual item (tab plus body) in a TabPanel.
45901  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
45902  * @param {String} id The id of this TabPanelItem
45903  * @param {String} text The text for the tab of this TabPanelItem
45904  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
45905  */
45906 Roo.bootstrap.panel.TabItem = function(config){
45907     /**
45908      * The {@link Roo.TabPanel} this TabPanelItem belongs to
45909      * @type Roo.TabPanel
45910      */
45911     this.tabPanel = config.panel;
45912     /**
45913      * The id for this TabPanelItem
45914      * @type String
45915      */
45916     this.id = config.id;
45917     /** @private */
45918     this.disabled = false;
45919     /** @private */
45920     this.text = config.text;
45921     /** @private */
45922     this.loaded = false;
45923     this.closable = config.closable;
45924
45925     /**
45926      * The body element for this TabPanelItem.
45927      * @type Roo.Element
45928      */
45929     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
45930     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
45931     this.bodyEl.setStyle("display", "block");
45932     this.bodyEl.setStyle("zoom", "1");
45933     //this.hideAction();
45934
45935     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
45936     /** @private */
45937     this.el = Roo.get(els.el);
45938     this.inner = Roo.get(els.inner, true);
45939      this.textEl = Roo.bootstrap.version == 4 ?
45940         this.el : Roo.get(this.el.dom.firstChild, true);
45941
45942     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
45943     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
45944
45945     
45946 //    this.el.on("mousedown", this.onTabMouseDown, this);
45947     this.el.on("click", this.onTabClick, this);
45948     /** @private */
45949     if(config.closable){
45950         var c = Roo.get(els.close, true);
45951         c.dom.title = this.closeText;
45952         c.addClassOnOver("close-over");
45953         c.on("click", this.closeClick, this);
45954      }
45955
45956     this.addEvents({
45957          /**
45958          * @event activate
45959          * Fires when this tab becomes the active tab.
45960          * @param {Roo.TabPanel} tabPanel The parent TabPanel
45961          * @param {Roo.TabPanelItem} this
45962          */
45963         "activate": true,
45964         /**
45965          * @event beforeclose
45966          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
45967          * @param {Roo.TabPanelItem} this
45968          * @param {Object} e Set cancel to true on this object to cancel the close.
45969          */
45970         "beforeclose": true,
45971         /**
45972          * @event close
45973          * Fires when this tab is closed.
45974          * @param {Roo.TabPanelItem} this
45975          */
45976          "close": true,
45977         /**
45978          * @event deactivate
45979          * Fires when this tab is no longer the active tab.
45980          * @param {Roo.TabPanel} tabPanel The parent TabPanel
45981          * @param {Roo.TabPanelItem} this
45982          */
45983          "deactivate" : true
45984     });
45985     this.hidden = false;
45986
45987     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
45988 };
45989
45990 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
45991            {
45992     purgeListeners : function(){
45993        Roo.util.Observable.prototype.purgeListeners.call(this);
45994        this.el.removeAllListeners();
45995     },
45996     /**
45997      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
45998      */
45999     show : function(){
46000         this.status_node.addClass("active");
46001         this.showAction();
46002         if(Roo.isOpera){
46003             this.tabPanel.stripWrap.repaint();
46004         }
46005         this.fireEvent("activate", this.tabPanel, this);
46006     },
46007
46008     /**
46009      * Returns true if this tab is the active tab.
46010      * @return {Boolean}
46011      */
46012     isActive : function(){
46013         return this.tabPanel.getActiveTab() == this;
46014     },
46015
46016     /**
46017      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46018      */
46019     hide : function(){
46020         this.status_node.removeClass("active");
46021         this.hideAction();
46022         this.fireEvent("deactivate", this.tabPanel, this);
46023     },
46024
46025     hideAction : function(){
46026         this.bodyEl.hide();
46027         this.bodyEl.setStyle("position", "absolute");
46028         this.bodyEl.setLeft("-20000px");
46029         this.bodyEl.setTop("-20000px");
46030     },
46031
46032     showAction : function(){
46033         this.bodyEl.setStyle("position", "relative");
46034         this.bodyEl.setTop("");
46035         this.bodyEl.setLeft("");
46036         this.bodyEl.show();
46037     },
46038
46039     /**
46040      * Set the tooltip for the tab.
46041      * @param {String} tooltip The tab's tooltip
46042      */
46043     setTooltip : function(text){
46044         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46045             this.textEl.dom.qtip = text;
46046             this.textEl.dom.removeAttribute('title');
46047         }else{
46048             this.textEl.dom.title = text;
46049         }
46050     },
46051
46052     onTabClick : function(e){
46053         e.preventDefault();
46054         this.tabPanel.activate(this.id);
46055     },
46056
46057     onTabMouseDown : function(e){
46058         e.preventDefault();
46059         this.tabPanel.activate(this.id);
46060     },
46061 /*
46062     getWidth : function(){
46063         return this.inner.getWidth();
46064     },
46065
46066     setWidth : function(width){
46067         var iwidth = width - this.linode.getPadding("lr");
46068         this.inner.setWidth(iwidth);
46069         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46070         this.linode.setWidth(width);
46071     },
46072 */
46073     /**
46074      * Show or hide the tab
46075      * @param {Boolean} hidden True to hide or false to show.
46076      */
46077     setHidden : function(hidden){
46078         this.hidden = hidden;
46079         this.linode.setStyle("display", hidden ? "none" : "");
46080     },
46081
46082     /**
46083      * Returns true if this tab is "hidden"
46084      * @return {Boolean}
46085      */
46086     isHidden : function(){
46087         return this.hidden;
46088     },
46089
46090     /**
46091      * Returns the text for this tab
46092      * @return {String}
46093      */
46094     getText : function(){
46095         return this.text;
46096     },
46097     /*
46098     autoSize : function(){
46099         //this.el.beginMeasure();
46100         this.textEl.setWidth(1);
46101         /*
46102          *  #2804 [new] Tabs in Roojs
46103          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46104          */
46105         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46106         //this.el.endMeasure();
46107     //},
46108
46109     /**
46110      * Sets the text for the tab (Note: this also sets the tooltip text)
46111      * @param {String} text The tab's text and tooltip
46112      */
46113     setText : function(text){
46114         this.text = text;
46115         this.textEl.update(text);
46116         this.setTooltip(text);
46117         //if(!this.tabPanel.resizeTabs){
46118         //    this.autoSize();
46119         //}
46120     },
46121     /**
46122      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46123      */
46124     activate : function(){
46125         this.tabPanel.activate(this.id);
46126     },
46127
46128     /**
46129      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46130      */
46131     disable : function(){
46132         if(this.tabPanel.active != this){
46133             this.disabled = true;
46134             this.status_node.addClass("disabled");
46135         }
46136     },
46137
46138     /**
46139      * Enables this TabPanelItem if it was previously disabled.
46140      */
46141     enable : function(){
46142         this.disabled = false;
46143         this.status_node.removeClass("disabled");
46144     },
46145
46146     /**
46147      * Sets the content for this TabPanelItem.
46148      * @param {String} content The content
46149      * @param {Boolean} loadScripts true to look for and load scripts
46150      */
46151     setContent : function(content, loadScripts){
46152         this.bodyEl.update(content, loadScripts);
46153     },
46154
46155     /**
46156      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46157      * @return {Roo.UpdateManager} The UpdateManager
46158      */
46159     getUpdateManager : function(){
46160         return this.bodyEl.getUpdateManager();
46161     },
46162
46163     /**
46164      * Set a URL to be used to load the content for this TabPanelItem.
46165      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46166      * @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)
46167      * @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)
46168      * @return {Roo.UpdateManager} The UpdateManager
46169      */
46170     setUrl : function(url, params, loadOnce){
46171         if(this.refreshDelegate){
46172             this.un('activate', this.refreshDelegate);
46173         }
46174         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46175         this.on("activate", this.refreshDelegate);
46176         return this.bodyEl.getUpdateManager();
46177     },
46178
46179     /** @private */
46180     _handleRefresh : function(url, params, loadOnce){
46181         if(!loadOnce || !this.loaded){
46182             var updater = this.bodyEl.getUpdateManager();
46183             updater.update(url, params, this._setLoaded.createDelegate(this));
46184         }
46185     },
46186
46187     /**
46188      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46189      *   Will fail silently if the setUrl method has not been called.
46190      *   This does not activate the panel, just updates its content.
46191      */
46192     refresh : function(){
46193         if(this.refreshDelegate){
46194            this.loaded = false;
46195            this.refreshDelegate();
46196         }
46197     },
46198
46199     /** @private */
46200     _setLoaded : function(){
46201         this.loaded = true;
46202     },
46203
46204     /** @private */
46205     closeClick : function(e){
46206         var o = {};
46207         e.stopEvent();
46208         this.fireEvent("beforeclose", this, o);
46209         if(o.cancel !== true){
46210             this.tabPanel.removeTab(this.id);
46211         }
46212     },
46213     /**
46214      * The text displayed in the tooltip for the close icon.
46215      * @type String
46216      */
46217     closeText : "Close this tab"
46218 });
46219 /**
46220 *    This script refer to:
46221 *    Title: International Telephone Input
46222 *    Author: Jack O'Connor
46223 *    Code version:  v12.1.12
46224 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46225 **/
46226
46227 Roo.bootstrap.form.PhoneInputData = function() {
46228     var d = [
46229       [
46230         "Afghanistan (‫افغانستان‬‎)",
46231         "af",
46232         "93"
46233       ],
46234       [
46235         "Albania (Shqipëri)",
46236         "al",
46237         "355"
46238       ],
46239       [
46240         "Algeria (‫الجزائر‬‎)",
46241         "dz",
46242         "213"
46243       ],
46244       [
46245         "American Samoa",
46246         "as",
46247         "1684"
46248       ],
46249       [
46250         "Andorra",
46251         "ad",
46252         "376"
46253       ],
46254       [
46255         "Angola",
46256         "ao",
46257         "244"
46258       ],
46259       [
46260         "Anguilla",
46261         "ai",
46262         "1264"
46263       ],
46264       [
46265         "Antigua and Barbuda",
46266         "ag",
46267         "1268"
46268       ],
46269       [
46270         "Argentina",
46271         "ar",
46272         "54"
46273       ],
46274       [
46275         "Armenia (Հայաստան)",
46276         "am",
46277         "374"
46278       ],
46279       [
46280         "Aruba",
46281         "aw",
46282         "297"
46283       ],
46284       [
46285         "Australia",
46286         "au",
46287         "61",
46288         0
46289       ],
46290       [
46291         "Austria (Österreich)",
46292         "at",
46293         "43"
46294       ],
46295       [
46296         "Azerbaijan (Azərbaycan)",
46297         "az",
46298         "994"
46299       ],
46300       [
46301         "Bahamas",
46302         "bs",
46303         "1242"
46304       ],
46305       [
46306         "Bahrain (‫البحرين‬‎)",
46307         "bh",
46308         "973"
46309       ],
46310       [
46311         "Bangladesh (বাংলাদেশ)",
46312         "bd",
46313         "880"
46314       ],
46315       [
46316         "Barbados",
46317         "bb",
46318         "1246"
46319       ],
46320       [
46321         "Belarus (Беларусь)",
46322         "by",
46323         "375"
46324       ],
46325       [
46326         "Belgium (België)",
46327         "be",
46328         "32"
46329       ],
46330       [
46331         "Belize",
46332         "bz",
46333         "501"
46334       ],
46335       [
46336         "Benin (Bénin)",
46337         "bj",
46338         "229"
46339       ],
46340       [
46341         "Bermuda",
46342         "bm",
46343         "1441"
46344       ],
46345       [
46346         "Bhutan (འབྲུག)",
46347         "bt",
46348         "975"
46349       ],
46350       [
46351         "Bolivia",
46352         "bo",
46353         "591"
46354       ],
46355       [
46356         "Bosnia and Herzegovina (Босна и Херцеговина)",
46357         "ba",
46358         "387"
46359       ],
46360       [
46361         "Botswana",
46362         "bw",
46363         "267"
46364       ],
46365       [
46366         "Brazil (Brasil)",
46367         "br",
46368         "55"
46369       ],
46370       [
46371         "British Indian Ocean Territory",
46372         "io",
46373         "246"
46374       ],
46375       [
46376         "British Virgin Islands",
46377         "vg",
46378         "1284"
46379       ],
46380       [
46381         "Brunei",
46382         "bn",
46383         "673"
46384       ],
46385       [
46386         "Bulgaria (България)",
46387         "bg",
46388         "359"
46389       ],
46390       [
46391         "Burkina Faso",
46392         "bf",
46393         "226"
46394       ],
46395       [
46396         "Burundi (Uburundi)",
46397         "bi",
46398         "257"
46399       ],
46400       [
46401         "Cambodia (កម្ពុជា)",
46402         "kh",
46403         "855"
46404       ],
46405       [
46406         "Cameroon (Cameroun)",
46407         "cm",
46408         "237"
46409       ],
46410       [
46411         "Canada",
46412         "ca",
46413         "1",
46414         1,
46415         ["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"]
46416       ],
46417       [
46418         "Cape Verde (Kabu Verdi)",
46419         "cv",
46420         "238"
46421       ],
46422       [
46423         "Caribbean Netherlands",
46424         "bq",
46425         "599",
46426         1
46427       ],
46428       [
46429         "Cayman Islands",
46430         "ky",
46431         "1345"
46432       ],
46433       [
46434         "Central African Republic (République centrafricaine)",
46435         "cf",
46436         "236"
46437       ],
46438       [
46439         "Chad (Tchad)",
46440         "td",
46441         "235"
46442       ],
46443       [
46444         "Chile",
46445         "cl",
46446         "56"
46447       ],
46448       [
46449         "China (中国)",
46450         "cn",
46451         "86"
46452       ],
46453       [
46454         "Christmas Island",
46455         "cx",
46456         "61",
46457         2
46458       ],
46459       [
46460         "Cocos (Keeling) Islands",
46461         "cc",
46462         "61",
46463         1
46464       ],
46465       [
46466         "Colombia",
46467         "co",
46468         "57"
46469       ],
46470       [
46471         "Comoros (‫جزر القمر‬‎)",
46472         "km",
46473         "269"
46474       ],
46475       [
46476         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46477         "cd",
46478         "243"
46479       ],
46480       [
46481         "Congo (Republic) (Congo-Brazzaville)",
46482         "cg",
46483         "242"
46484       ],
46485       [
46486         "Cook Islands",
46487         "ck",
46488         "682"
46489       ],
46490       [
46491         "Costa Rica",
46492         "cr",
46493         "506"
46494       ],
46495       [
46496         "Côte d’Ivoire",
46497         "ci",
46498         "225"
46499       ],
46500       [
46501         "Croatia (Hrvatska)",
46502         "hr",
46503         "385"
46504       ],
46505       [
46506         "Cuba",
46507         "cu",
46508         "53"
46509       ],
46510       [
46511         "Curaçao",
46512         "cw",
46513         "599",
46514         0
46515       ],
46516       [
46517         "Cyprus (Κύπρος)",
46518         "cy",
46519         "357"
46520       ],
46521       [
46522         "Czech Republic (Česká republika)",
46523         "cz",
46524         "420"
46525       ],
46526       [
46527         "Denmark (Danmark)",
46528         "dk",
46529         "45"
46530       ],
46531       [
46532         "Djibouti",
46533         "dj",
46534         "253"
46535       ],
46536       [
46537         "Dominica",
46538         "dm",
46539         "1767"
46540       ],
46541       [
46542         "Dominican Republic (República Dominicana)",
46543         "do",
46544         "1",
46545         2,
46546         ["809", "829", "849"]
46547       ],
46548       [
46549         "Ecuador",
46550         "ec",
46551         "593"
46552       ],
46553       [
46554         "Egypt (‫مصر‬‎)",
46555         "eg",
46556         "20"
46557       ],
46558       [
46559         "El Salvador",
46560         "sv",
46561         "503"
46562       ],
46563       [
46564         "Equatorial Guinea (Guinea Ecuatorial)",
46565         "gq",
46566         "240"
46567       ],
46568       [
46569         "Eritrea",
46570         "er",
46571         "291"
46572       ],
46573       [
46574         "Estonia (Eesti)",
46575         "ee",
46576         "372"
46577       ],
46578       [
46579         "Ethiopia",
46580         "et",
46581         "251"
46582       ],
46583       [
46584         "Falkland Islands (Islas Malvinas)",
46585         "fk",
46586         "500"
46587       ],
46588       [
46589         "Faroe Islands (Føroyar)",
46590         "fo",
46591         "298"
46592       ],
46593       [
46594         "Fiji",
46595         "fj",
46596         "679"
46597       ],
46598       [
46599         "Finland (Suomi)",
46600         "fi",
46601         "358",
46602         0
46603       ],
46604       [
46605         "France",
46606         "fr",
46607         "33"
46608       ],
46609       [
46610         "French Guiana (Guyane française)",
46611         "gf",
46612         "594"
46613       ],
46614       [
46615         "French Polynesia (Polynésie française)",
46616         "pf",
46617         "689"
46618       ],
46619       [
46620         "Gabon",
46621         "ga",
46622         "241"
46623       ],
46624       [
46625         "Gambia",
46626         "gm",
46627         "220"
46628       ],
46629       [
46630         "Georgia (საქართველო)",
46631         "ge",
46632         "995"
46633       ],
46634       [
46635         "Germany (Deutschland)",
46636         "de",
46637         "49"
46638       ],
46639       [
46640         "Ghana (Gaana)",
46641         "gh",
46642         "233"
46643       ],
46644       [
46645         "Gibraltar",
46646         "gi",
46647         "350"
46648       ],
46649       [
46650         "Greece (Ελλάδα)",
46651         "gr",
46652         "30"
46653       ],
46654       [
46655         "Greenland (Kalaallit Nunaat)",
46656         "gl",
46657         "299"
46658       ],
46659       [
46660         "Grenada",
46661         "gd",
46662         "1473"
46663       ],
46664       [
46665         "Guadeloupe",
46666         "gp",
46667         "590",
46668         0
46669       ],
46670       [
46671         "Guam",
46672         "gu",
46673         "1671"
46674       ],
46675       [
46676         "Guatemala",
46677         "gt",
46678         "502"
46679       ],
46680       [
46681         "Guernsey",
46682         "gg",
46683         "44",
46684         1
46685       ],
46686       [
46687         "Guinea (Guinée)",
46688         "gn",
46689         "224"
46690       ],
46691       [
46692         "Guinea-Bissau (Guiné Bissau)",
46693         "gw",
46694         "245"
46695       ],
46696       [
46697         "Guyana",
46698         "gy",
46699         "592"
46700       ],
46701       [
46702         "Haiti",
46703         "ht",
46704         "509"
46705       ],
46706       [
46707         "Honduras",
46708         "hn",
46709         "504"
46710       ],
46711       [
46712         "Hong Kong (香港)",
46713         "hk",
46714         "852"
46715       ],
46716       [
46717         "Hungary (Magyarország)",
46718         "hu",
46719         "36"
46720       ],
46721       [
46722         "Iceland (Ísland)",
46723         "is",
46724         "354"
46725       ],
46726       [
46727         "India (भारत)",
46728         "in",
46729         "91"
46730       ],
46731       [
46732         "Indonesia",
46733         "id",
46734         "62"
46735       ],
46736       [
46737         "Iran (‫ایران‬‎)",
46738         "ir",
46739         "98"
46740       ],
46741       [
46742         "Iraq (‫العراق‬‎)",
46743         "iq",
46744         "964"
46745       ],
46746       [
46747         "Ireland",
46748         "ie",
46749         "353"
46750       ],
46751       [
46752         "Isle of Man",
46753         "im",
46754         "44",
46755         2
46756       ],
46757       [
46758         "Israel (‫ישראל‬‎)",
46759         "il",
46760         "972"
46761       ],
46762       [
46763         "Italy (Italia)",
46764         "it",
46765         "39",
46766         0
46767       ],
46768       [
46769         "Jamaica",
46770         "jm",
46771         "1876"
46772       ],
46773       [
46774         "Japan (日本)",
46775         "jp",
46776         "81"
46777       ],
46778       [
46779         "Jersey",
46780         "je",
46781         "44",
46782         3
46783       ],
46784       [
46785         "Jordan (‫الأردن‬‎)",
46786         "jo",
46787         "962"
46788       ],
46789       [
46790         "Kazakhstan (Казахстан)",
46791         "kz",
46792         "7",
46793         1
46794       ],
46795       [
46796         "Kenya",
46797         "ke",
46798         "254"
46799       ],
46800       [
46801         "Kiribati",
46802         "ki",
46803         "686"
46804       ],
46805       [
46806         "Kosovo",
46807         "xk",
46808         "383"
46809       ],
46810       [
46811         "Kuwait (‫الكويت‬‎)",
46812         "kw",
46813         "965"
46814       ],
46815       [
46816         "Kyrgyzstan (Кыргызстан)",
46817         "kg",
46818         "996"
46819       ],
46820       [
46821         "Laos (ລາວ)",
46822         "la",
46823         "856"
46824       ],
46825       [
46826         "Latvia (Latvija)",
46827         "lv",
46828         "371"
46829       ],
46830       [
46831         "Lebanon (‫لبنان‬‎)",
46832         "lb",
46833         "961"
46834       ],
46835       [
46836         "Lesotho",
46837         "ls",
46838         "266"
46839       ],
46840       [
46841         "Liberia",
46842         "lr",
46843         "231"
46844       ],
46845       [
46846         "Libya (‫ليبيا‬‎)",
46847         "ly",
46848         "218"
46849       ],
46850       [
46851         "Liechtenstein",
46852         "li",
46853         "423"
46854       ],
46855       [
46856         "Lithuania (Lietuva)",
46857         "lt",
46858         "370"
46859       ],
46860       [
46861         "Luxembourg",
46862         "lu",
46863         "352"
46864       ],
46865       [
46866         "Macau (澳門)",
46867         "mo",
46868         "853"
46869       ],
46870       [
46871         "Macedonia (FYROM) (Македонија)",
46872         "mk",
46873         "389"
46874       ],
46875       [
46876         "Madagascar (Madagasikara)",
46877         "mg",
46878         "261"
46879       ],
46880       [
46881         "Malawi",
46882         "mw",
46883         "265"
46884       ],
46885       [
46886         "Malaysia",
46887         "my",
46888         "60"
46889       ],
46890       [
46891         "Maldives",
46892         "mv",
46893         "960"
46894       ],
46895       [
46896         "Mali",
46897         "ml",
46898         "223"
46899       ],
46900       [
46901         "Malta",
46902         "mt",
46903         "356"
46904       ],
46905       [
46906         "Marshall Islands",
46907         "mh",
46908         "692"
46909       ],
46910       [
46911         "Martinique",
46912         "mq",
46913         "596"
46914       ],
46915       [
46916         "Mauritania (‫موريتانيا‬‎)",
46917         "mr",
46918         "222"
46919       ],
46920       [
46921         "Mauritius (Moris)",
46922         "mu",
46923         "230"
46924       ],
46925       [
46926         "Mayotte",
46927         "yt",
46928         "262",
46929         1
46930       ],
46931       [
46932         "Mexico (México)",
46933         "mx",
46934         "52"
46935       ],
46936       [
46937         "Micronesia",
46938         "fm",
46939         "691"
46940       ],
46941       [
46942         "Moldova (Republica Moldova)",
46943         "md",
46944         "373"
46945       ],
46946       [
46947         "Monaco",
46948         "mc",
46949         "377"
46950       ],
46951       [
46952         "Mongolia (Монгол)",
46953         "mn",
46954         "976"
46955       ],
46956       [
46957         "Montenegro (Crna Gora)",
46958         "me",
46959         "382"
46960       ],
46961       [
46962         "Montserrat",
46963         "ms",
46964         "1664"
46965       ],
46966       [
46967         "Morocco (‫المغرب‬‎)",
46968         "ma",
46969         "212",
46970         0
46971       ],
46972       [
46973         "Mozambique (Moçambique)",
46974         "mz",
46975         "258"
46976       ],
46977       [
46978         "Myanmar (Burma) (မြန်မာ)",
46979         "mm",
46980         "95"
46981       ],
46982       [
46983         "Namibia (Namibië)",
46984         "na",
46985         "264"
46986       ],
46987       [
46988         "Nauru",
46989         "nr",
46990         "674"
46991       ],
46992       [
46993         "Nepal (नेपाल)",
46994         "np",
46995         "977"
46996       ],
46997       [
46998         "Netherlands (Nederland)",
46999         "nl",
47000         "31"
47001       ],
47002       [
47003         "New Caledonia (Nouvelle-Calédonie)",
47004         "nc",
47005         "687"
47006       ],
47007       [
47008         "New Zealand",
47009         "nz",
47010         "64"
47011       ],
47012       [
47013         "Nicaragua",
47014         "ni",
47015         "505"
47016       ],
47017       [
47018         "Niger (Nijar)",
47019         "ne",
47020         "227"
47021       ],
47022       [
47023         "Nigeria",
47024         "ng",
47025         "234"
47026       ],
47027       [
47028         "Niue",
47029         "nu",
47030         "683"
47031       ],
47032       [
47033         "Norfolk Island",
47034         "nf",
47035         "672"
47036       ],
47037       [
47038         "North Korea (조선 민주주의 인민 공화국)",
47039         "kp",
47040         "850"
47041       ],
47042       [
47043         "Northern Mariana Islands",
47044         "mp",
47045         "1670"
47046       ],
47047       [
47048         "Norway (Norge)",
47049         "no",
47050         "47",
47051         0
47052       ],
47053       [
47054         "Oman (‫عُمان‬‎)",
47055         "om",
47056         "968"
47057       ],
47058       [
47059         "Pakistan (‫پاکستان‬‎)",
47060         "pk",
47061         "92"
47062       ],
47063       [
47064         "Palau",
47065         "pw",
47066         "680"
47067       ],
47068       [
47069         "Palestine (‫فلسطين‬‎)",
47070         "ps",
47071         "970"
47072       ],
47073       [
47074         "Panama (Panamá)",
47075         "pa",
47076         "507"
47077       ],
47078       [
47079         "Papua New Guinea",
47080         "pg",
47081         "675"
47082       ],
47083       [
47084         "Paraguay",
47085         "py",
47086         "595"
47087       ],
47088       [
47089         "Peru (Perú)",
47090         "pe",
47091         "51"
47092       ],
47093       [
47094         "Philippines",
47095         "ph",
47096         "63"
47097       ],
47098       [
47099         "Poland (Polska)",
47100         "pl",
47101         "48"
47102       ],
47103       [
47104         "Portugal",
47105         "pt",
47106         "351"
47107       ],
47108       [
47109         "Puerto Rico",
47110         "pr",
47111         "1",
47112         3,
47113         ["787", "939"]
47114       ],
47115       [
47116         "Qatar (‫قطر‬‎)",
47117         "qa",
47118         "974"
47119       ],
47120       [
47121         "Réunion (La Réunion)",
47122         "re",
47123         "262",
47124         0
47125       ],
47126       [
47127         "Romania (România)",
47128         "ro",
47129         "40"
47130       ],
47131       [
47132         "Russia (Россия)",
47133         "ru",
47134         "7",
47135         0
47136       ],
47137       [
47138         "Rwanda",
47139         "rw",
47140         "250"
47141       ],
47142       [
47143         "Saint Barthélemy",
47144         "bl",
47145         "590",
47146         1
47147       ],
47148       [
47149         "Saint Helena",
47150         "sh",
47151         "290"
47152       ],
47153       [
47154         "Saint Kitts and Nevis",
47155         "kn",
47156         "1869"
47157       ],
47158       [
47159         "Saint Lucia",
47160         "lc",
47161         "1758"
47162       ],
47163       [
47164         "Saint Martin (Saint-Martin (partie française))",
47165         "mf",
47166         "590",
47167         2
47168       ],
47169       [
47170         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47171         "pm",
47172         "508"
47173       ],
47174       [
47175         "Saint Vincent and the Grenadines",
47176         "vc",
47177         "1784"
47178       ],
47179       [
47180         "Samoa",
47181         "ws",
47182         "685"
47183       ],
47184       [
47185         "San Marino",
47186         "sm",
47187         "378"
47188       ],
47189       [
47190         "São Tomé and Príncipe (São Tomé e Príncipe)",
47191         "st",
47192         "239"
47193       ],
47194       [
47195         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47196         "sa",
47197         "966"
47198       ],
47199       [
47200         "Senegal (Sénégal)",
47201         "sn",
47202         "221"
47203       ],
47204       [
47205         "Serbia (Србија)",
47206         "rs",
47207         "381"
47208       ],
47209       [
47210         "Seychelles",
47211         "sc",
47212         "248"
47213       ],
47214       [
47215         "Sierra Leone",
47216         "sl",
47217         "232"
47218       ],
47219       [
47220         "Singapore",
47221         "sg",
47222         "65"
47223       ],
47224       [
47225         "Sint Maarten",
47226         "sx",
47227         "1721"
47228       ],
47229       [
47230         "Slovakia (Slovensko)",
47231         "sk",
47232         "421"
47233       ],
47234       [
47235         "Slovenia (Slovenija)",
47236         "si",
47237         "386"
47238       ],
47239       [
47240         "Solomon Islands",
47241         "sb",
47242         "677"
47243       ],
47244       [
47245         "Somalia (Soomaaliya)",
47246         "so",
47247         "252"
47248       ],
47249       [
47250         "South Africa",
47251         "za",
47252         "27"
47253       ],
47254       [
47255         "South Korea (대한민국)",
47256         "kr",
47257         "82"
47258       ],
47259       [
47260         "South Sudan (‫جنوب السودان‬‎)",
47261         "ss",
47262         "211"
47263       ],
47264       [
47265         "Spain (España)",
47266         "es",
47267         "34"
47268       ],
47269       [
47270         "Sri Lanka (ශ්‍රී ලංකාව)",
47271         "lk",
47272         "94"
47273       ],
47274       [
47275         "Sudan (‫السودان‬‎)",
47276         "sd",
47277         "249"
47278       ],
47279       [
47280         "Suriname",
47281         "sr",
47282         "597"
47283       ],
47284       [
47285         "Svalbard and Jan Mayen",
47286         "sj",
47287         "47",
47288         1
47289       ],
47290       [
47291         "Swaziland",
47292         "sz",
47293         "268"
47294       ],
47295       [
47296         "Sweden (Sverige)",
47297         "se",
47298         "46"
47299       ],
47300       [
47301         "Switzerland (Schweiz)",
47302         "ch",
47303         "41"
47304       ],
47305       [
47306         "Syria (‫سوريا‬‎)",
47307         "sy",
47308         "963"
47309       ],
47310       [
47311         "Taiwan (台灣)",
47312         "tw",
47313         "886"
47314       ],
47315       [
47316         "Tajikistan",
47317         "tj",
47318         "992"
47319       ],
47320       [
47321         "Tanzania",
47322         "tz",
47323         "255"
47324       ],
47325       [
47326         "Thailand (ไทย)",
47327         "th",
47328         "66"
47329       ],
47330       [
47331         "Timor-Leste",
47332         "tl",
47333         "670"
47334       ],
47335       [
47336         "Togo",
47337         "tg",
47338         "228"
47339       ],
47340       [
47341         "Tokelau",
47342         "tk",
47343         "690"
47344       ],
47345       [
47346         "Tonga",
47347         "to",
47348         "676"
47349       ],
47350       [
47351         "Trinidad and Tobago",
47352         "tt",
47353         "1868"
47354       ],
47355       [
47356         "Tunisia (‫تونس‬‎)",
47357         "tn",
47358         "216"
47359       ],
47360       [
47361         "Turkey (Türkiye)",
47362         "tr",
47363         "90"
47364       ],
47365       [
47366         "Turkmenistan",
47367         "tm",
47368         "993"
47369       ],
47370       [
47371         "Turks and Caicos Islands",
47372         "tc",
47373         "1649"
47374       ],
47375       [
47376         "Tuvalu",
47377         "tv",
47378         "688"
47379       ],
47380       [
47381         "U.S. Virgin Islands",
47382         "vi",
47383         "1340"
47384       ],
47385       [
47386         "Uganda",
47387         "ug",
47388         "256"
47389       ],
47390       [
47391         "Ukraine (Україна)",
47392         "ua",
47393         "380"
47394       ],
47395       [
47396         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47397         "ae",
47398         "971"
47399       ],
47400       [
47401         "United Kingdom",
47402         "gb",
47403         "44",
47404         0
47405       ],
47406       [
47407         "United States",
47408         "us",
47409         "1",
47410         0
47411       ],
47412       [
47413         "Uruguay",
47414         "uy",
47415         "598"
47416       ],
47417       [
47418         "Uzbekistan (Oʻzbekiston)",
47419         "uz",
47420         "998"
47421       ],
47422       [
47423         "Vanuatu",
47424         "vu",
47425         "678"
47426       ],
47427       [
47428         "Vatican City (Città del Vaticano)",
47429         "va",
47430         "39",
47431         1
47432       ],
47433       [
47434         "Venezuela",
47435         "ve",
47436         "58"
47437       ],
47438       [
47439         "Vietnam (Việt Nam)",
47440         "vn",
47441         "84"
47442       ],
47443       [
47444         "Wallis and Futuna (Wallis-et-Futuna)",
47445         "wf",
47446         "681"
47447       ],
47448       [
47449         "Western Sahara (‫الصحراء الغربية‬‎)",
47450         "eh",
47451         "212",
47452         1
47453       ],
47454       [
47455         "Yemen (‫اليمن‬‎)",
47456         "ye",
47457         "967"
47458       ],
47459       [
47460         "Zambia",
47461         "zm",
47462         "260"
47463       ],
47464       [
47465         "Zimbabwe",
47466         "zw",
47467         "263"
47468       ],
47469       [
47470         "Åland Islands",
47471         "ax",
47472         "358",
47473         1
47474       ]
47475   ];
47476   
47477   return d;
47478 }/**
47479 *    This script refer to:
47480 *    Title: International Telephone Input
47481 *    Author: Jack O'Connor
47482 *    Code version:  v12.1.12
47483 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47484 **/
47485
47486 /**
47487  * @class Roo.bootstrap.form.PhoneInput
47488  * @extends Roo.bootstrap.form.TriggerField
47489  * An input with International dial-code selection
47490  
47491  * @cfg {String} defaultDialCode default '+852'
47492  * @cfg {Array} preferedCountries default []
47493   
47494  * @constructor
47495  * Create a new PhoneInput.
47496  * @param {Object} config Configuration options
47497  */
47498
47499 Roo.bootstrap.form.PhoneInput = function(config) {
47500     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47501 };
47502
47503 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47504         /**
47505         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47506         */
47507         listWidth: undefined,
47508         
47509         selectedClass: 'active',
47510         
47511         invalidClass : "has-warning",
47512         
47513         validClass: 'has-success',
47514         
47515         allowed: '0123456789',
47516         
47517         max_length: 15,
47518         
47519         /**
47520          * @cfg {String} defaultDialCode The default dial code when initializing the input
47521          */
47522         defaultDialCode: '+852',
47523         
47524         /**
47525          * @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
47526          */
47527         preferedCountries: false,
47528         
47529         getAutoCreate : function()
47530         {
47531             var data = Roo.bootstrap.form.PhoneInputData();
47532             var align = this.labelAlign || this.parentLabelAlign();
47533             var id = Roo.id();
47534             
47535             this.allCountries = [];
47536             this.dialCodeMapping = [];
47537             
47538             for (var i = 0; i < data.length; i++) {
47539               var c = data[i];
47540               this.allCountries[i] = {
47541                 name: c[0],
47542                 iso2: c[1],
47543                 dialCode: c[2],
47544                 priority: c[3] || 0,
47545                 areaCodes: c[4] || null
47546               };
47547               this.dialCodeMapping[c[2]] = {
47548                   name: c[0],
47549                   iso2: c[1],
47550                   priority: c[3] || 0,
47551                   areaCodes: c[4] || null
47552               };
47553             }
47554             
47555             var cfg = {
47556                 cls: 'form-group',
47557                 cn: []
47558             };
47559             
47560             var input =  {
47561                 tag: 'input',
47562                 id : id,
47563                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
47564                 maxlength: this.max_length,
47565                 cls : 'form-control tel-input',
47566                 autocomplete: 'new-password'
47567             };
47568             
47569             var hiddenInput = {
47570                 tag: 'input',
47571                 type: 'hidden',
47572                 cls: 'hidden-tel-input'
47573             };
47574             
47575             if (this.name) {
47576                 hiddenInput.name = this.name;
47577             }
47578             
47579             if (this.disabled) {
47580                 input.disabled = true;
47581             }
47582             
47583             var flag_container = {
47584                 tag: 'div',
47585                 cls: 'flag-box',
47586                 cn: [
47587                     {
47588                         tag: 'div',
47589                         cls: 'flag'
47590                     },
47591                     {
47592                         tag: 'div',
47593                         cls: 'caret'
47594                     }
47595                 ]
47596             };
47597             
47598             var box = {
47599                 tag: 'div',
47600                 cls: this.hasFeedback ? 'has-feedback' : '',
47601                 cn: [
47602                     hiddenInput,
47603                     input,
47604                     {
47605                         tag: 'input',
47606                         cls: 'dial-code-holder',
47607                         disabled: true
47608                     }
47609                 ]
47610             };
47611             
47612             var container = {
47613                 cls: 'roo-select2-container input-group',
47614                 cn: [
47615                     flag_container,
47616                     box
47617                 ]
47618             };
47619             
47620             if (this.fieldLabel.length) {
47621                 var indicator = {
47622                     tag: 'i',
47623                     tooltip: 'This field is required'
47624                 };
47625                 
47626                 var label = {
47627                     tag: 'label',
47628                     'for':  id,
47629                     cls: 'control-label',
47630                     cn: []
47631                 };
47632                 
47633                 var label_text = {
47634                     tag: 'span',
47635                     html: this.fieldLabel
47636                 };
47637                 
47638                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
47639                 label.cn = [
47640                     indicator,
47641                     label_text
47642                 ];
47643                 
47644                 if(this.indicatorpos == 'right') {
47645                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
47646                     label.cn = [
47647                         label_text,
47648                         indicator
47649                     ];
47650                 }
47651                 
47652                 if(align == 'left') {
47653                     container = {
47654                         tag: 'div',
47655                         cn: [
47656                             container
47657                         ]
47658                     };
47659                     
47660                     if(this.labelWidth > 12){
47661                         label.style = "width: " + this.labelWidth + 'px';
47662                     }
47663                     if(this.labelWidth < 13 && this.labelmd == 0){
47664                         this.labelmd = this.labelWidth;
47665                     }
47666                     if(this.labellg > 0){
47667                         label.cls += ' col-lg-' + this.labellg;
47668                         input.cls += ' col-lg-' + (12 - this.labellg);
47669                     }
47670                     if(this.labelmd > 0){
47671                         label.cls += ' col-md-' + this.labelmd;
47672                         container.cls += ' col-md-' + (12 - this.labelmd);
47673                     }
47674                     if(this.labelsm > 0){
47675                         label.cls += ' col-sm-' + this.labelsm;
47676                         container.cls += ' col-sm-' + (12 - this.labelsm);
47677                     }
47678                     if(this.labelxs > 0){
47679                         label.cls += ' col-xs-' + this.labelxs;
47680                         container.cls += ' col-xs-' + (12 - this.labelxs);
47681                     }
47682                 }
47683             }
47684             
47685             cfg.cn = [
47686                 label,
47687                 container
47688             ];
47689             
47690             var settings = this;
47691             
47692             ['xs','sm','md','lg'].map(function(size){
47693                 if (settings[size]) {
47694                     cfg.cls += ' col-' + size + '-' + settings[size];
47695                 }
47696             });
47697             
47698             this.store = new Roo.data.Store({
47699                 proxy : new Roo.data.MemoryProxy({}),
47700                 reader : new Roo.data.JsonReader({
47701                     fields : [
47702                         {
47703                             'name' : 'name',
47704                             'type' : 'string'
47705                         },
47706                         {
47707                             'name' : 'iso2',
47708                             'type' : 'string'
47709                         },
47710                         {
47711                             'name' : 'dialCode',
47712                             'type' : 'string'
47713                         },
47714                         {
47715                             'name' : 'priority',
47716                             'type' : 'string'
47717                         },
47718                         {
47719                             'name' : 'areaCodes',
47720                             'type' : 'string'
47721                         }
47722                     ]
47723                 })
47724             });
47725             
47726             if(!this.preferedCountries) {
47727                 this.preferedCountries = [
47728                     'hk',
47729                     'gb',
47730                     'us'
47731                 ];
47732             }
47733             
47734             var p = this.preferedCountries.reverse();
47735             
47736             if(p) {
47737                 for (var i = 0; i < p.length; i++) {
47738                     for (var j = 0; j < this.allCountries.length; j++) {
47739                         if(this.allCountries[j].iso2 == p[i]) {
47740                             var t = this.allCountries[j];
47741                             this.allCountries.splice(j,1);
47742                             this.allCountries.unshift(t);
47743                         }
47744                     } 
47745                 }
47746             }
47747             
47748             this.store.proxy.data = {
47749                 success: true,
47750                 data: this.allCountries
47751             };
47752             
47753             return cfg;
47754         },
47755         
47756         initEvents : function()
47757         {
47758             this.createList();
47759             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
47760             
47761             this.indicator = this.indicatorEl();
47762             this.flag = this.flagEl();
47763             this.dialCodeHolder = this.dialCodeHolderEl();
47764             
47765             this.trigger = this.el.select('div.flag-box',true).first();
47766             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
47767             
47768             var _this = this;
47769             
47770             (function(){
47771                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47772                 _this.list.setWidth(lw);
47773             }).defer(100);
47774             
47775             this.list.on('mouseover', this.onViewOver, this);
47776             this.list.on('mousemove', this.onViewMove, this);
47777             this.inputEl().on("keyup", this.onKeyUp, this);
47778             this.inputEl().on("keypress", this.onKeyPress, this);
47779             
47780             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
47781
47782             this.view = new Roo.View(this.list, this.tpl, {
47783                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47784             });
47785             
47786             this.view.on('click', this.onViewClick, this);
47787             this.setValue(this.defaultDialCode);
47788         },
47789         
47790         onTriggerClick : function(e)
47791         {
47792             Roo.log('trigger click');
47793             if(this.disabled){
47794                 return;
47795             }
47796             
47797             if(this.isExpanded()){
47798                 this.collapse();
47799                 this.hasFocus = false;
47800             }else {
47801                 this.store.load({});
47802                 this.hasFocus = true;
47803                 this.expand();
47804             }
47805         },
47806         
47807         isExpanded : function()
47808         {
47809             return this.list.isVisible();
47810         },
47811         
47812         collapse : function()
47813         {
47814             if(!this.isExpanded()){
47815                 return;
47816             }
47817             this.list.hide();
47818             Roo.get(document).un('mousedown', this.collapseIf, this);
47819             Roo.get(document).un('mousewheel', this.collapseIf, this);
47820             this.fireEvent('collapse', this);
47821             this.validate();
47822         },
47823         
47824         expand : function()
47825         {
47826             Roo.log('expand');
47827
47828             if(this.isExpanded() || !this.hasFocus){
47829                 return;
47830             }
47831             
47832             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
47833             this.list.setWidth(lw);
47834             
47835             this.list.show();
47836             this.restrictHeight();
47837             
47838             Roo.get(document).on('mousedown', this.collapseIf, this);
47839             Roo.get(document).on('mousewheel', this.collapseIf, this);
47840             
47841             this.fireEvent('expand', this);
47842         },
47843         
47844         restrictHeight : function()
47845         {
47846             this.list.alignTo(this.inputEl(), this.listAlign);
47847             this.list.alignTo(this.inputEl(), this.listAlign);
47848         },
47849         
47850         onViewOver : function(e, t)
47851         {
47852             if(this.inKeyMode){
47853                 return;
47854             }
47855             var item = this.view.findItemFromChild(t);
47856             
47857             if(item){
47858                 var index = this.view.indexOf(item);
47859                 this.select(index, false);
47860             }
47861         },
47862
47863         // private
47864         onViewClick : function(view, doFocus, el, e)
47865         {
47866             var index = this.view.getSelectedIndexes()[0];
47867             
47868             var r = this.store.getAt(index);
47869             
47870             if(r){
47871                 this.onSelect(r, index);
47872             }
47873             if(doFocus !== false && !this.blockFocus){
47874                 this.inputEl().focus();
47875             }
47876         },
47877         
47878         onViewMove : function(e, t)
47879         {
47880             this.inKeyMode = false;
47881         },
47882         
47883         select : function(index, scrollIntoView)
47884         {
47885             this.selectedIndex = index;
47886             this.view.select(index);
47887             if(scrollIntoView !== false){
47888                 var el = this.view.getNode(index);
47889                 if(el){
47890                     this.list.scrollChildIntoView(el, false);
47891                 }
47892             }
47893         },
47894         
47895         createList : function()
47896         {
47897             this.list = Roo.get(document.body).createChild({
47898                 tag: 'ul',
47899                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
47900                 style: 'display:none'
47901             });
47902             
47903             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
47904         },
47905         
47906         collapseIf : function(e)
47907         {
47908             var in_combo  = e.within(this.el);
47909             var in_list =  e.within(this.list);
47910             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
47911             
47912             if (in_combo || in_list || is_list) {
47913                 return;
47914             }
47915             this.collapse();
47916         },
47917         
47918         onSelect : function(record, index)
47919         {
47920             if(this.fireEvent('beforeselect', this, record, index) !== false){
47921                 
47922                 this.setFlagClass(record.data.iso2);
47923                 this.setDialCode(record.data.dialCode);
47924                 this.hasFocus = false;
47925                 this.collapse();
47926                 this.fireEvent('select', this, record, index);
47927             }
47928         },
47929         
47930         flagEl : function()
47931         {
47932             var flag = this.el.select('div.flag',true).first();
47933             if(!flag){
47934                 return false;
47935             }
47936             return flag;
47937         },
47938         
47939         dialCodeHolderEl : function()
47940         {
47941             var d = this.el.select('input.dial-code-holder',true).first();
47942             if(!d){
47943                 return false;
47944             }
47945             return d;
47946         },
47947         
47948         setDialCode : function(v)
47949         {
47950             this.dialCodeHolder.dom.value = '+'+v;
47951         },
47952         
47953         setFlagClass : function(n)
47954         {
47955             this.flag.dom.className = 'flag '+n;
47956         },
47957         
47958         getValue : function()
47959         {
47960             var v = this.inputEl().getValue();
47961             if(this.dialCodeHolder) {
47962                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
47963             }
47964             return v;
47965         },
47966         
47967         setValue : function(v)
47968         {
47969             var d = this.getDialCode(v);
47970             
47971             //invalid dial code
47972             if(v.length == 0 || !d || d.length == 0) {
47973                 if(this.rendered){
47974                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
47975                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47976                 }
47977                 return;
47978             }
47979             
47980             //valid dial code
47981             this.setFlagClass(this.dialCodeMapping[d].iso2);
47982             this.setDialCode(d);
47983             this.inputEl().dom.value = v.replace('+'+d,'');
47984             this.hiddenEl().dom.value = this.getValue();
47985             
47986             this.validate();
47987         },
47988         
47989         getDialCode : function(v)
47990         {
47991             v = v ||  '';
47992             
47993             if (v.length == 0) {
47994                 return this.dialCodeHolder.dom.value;
47995             }
47996             
47997             var dialCode = "";
47998             if (v.charAt(0) != "+") {
47999                 return false;
48000             }
48001             var numericChars = "";
48002             for (var i = 1; i < v.length; i++) {
48003               var c = v.charAt(i);
48004               if (!isNaN(c)) {
48005                 numericChars += c;
48006                 if (this.dialCodeMapping[numericChars]) {
48007                   dialCode = v.substr(1, i);
48008                 }
48009                 if (numericChars.length == 4) {
48010                   break;
48011                 }
48012               }
48013             }
48014             return dialCode;
48015         },
48016         
48017         reset : function()
48018         {
48019             this.setValue(this.defaultDialCode);
48020             this.validate();
48021         },
48022         
48023         hiddenEl : function()
48024         {
48025             return this.el.select('input.hidden-tel-input',true).first();
48026         },
48027         
48028         // after setting val
48029         onKeyUp : function(e){
48030             this.setValue(this.getValue());
48031         },
48032         
48033         onKeyPress : function(e){
48034             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48035                 e.stopEvent();
48036             }
48037         }
48038         
48039 });
48040 /**
48041  * @class Roo.bootstrap.form.MoneyField
48042  * @extends Roo.bootstrap.form.ComboBox
48043  * Bootstrap MoneyField class
48044  * 
48045  * @constructor
48046  * Create a new MoneyField.
48047  * @param {Object} config Configuration options
48048  */
48049
48050 Roo.bootstrap.form.MoneyField = function(config) {
48051     
48052     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48053     
48054 };
48055
48056 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48057     
48058     /**
48059      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48060      */
48061     allowDecimals : true,
48062     /**
48063      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48064      */
48065     decimalSeparator : ".",
48066     /**
48067      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48068      */
48069     decimalPrecision : 0,
48070     /**
48071      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48072      */
48073     allowNegative : true,
48074     /**
48075      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48076      */
48077     allowZero: true,
48078     /**
48079      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48080      */
48081     minValue : Number.NEGATIVE_INFINITY,
48082     /**
48083      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48084      */
48085     maxValue : Number.MAX_VALUE,
48086     /**
48087      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48088      */
48089     minText : "The minimum value for this field is {0}",
48090     /**
48091      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48092      */
48093     maxText : "The maximum value for this field is {0}",
48094     /**
48095      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48096      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48097      */
48098     nanText : "{0} is not a valid number",
48099     /**
48100      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48101      */
48102     castInt : true,
48103     /**
48104      * @cfg {String} defaults currency of the MoneyField
48105      * value should be in lkey
48106      */
48107     defaultCurrency : false,
48108     /**
48109      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48110      */
48111     thousandsDelimiter : false,
48112     /**
48113      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48114      */
48115     max_length: false,
48116     
48117     inputlg : 9,
48118     inputmd : 9,
48119     inputsm : 9,
48120     inputxs : 6,
48121      /**
48122      * @cfg {Roo.data.Store} store  Store to lookup currency??
48123      */
48124     store : false,
48125     
48126     getAutoCreate : function()
48127     {
48128         var align = this.labelAlign || this.parentLabelAlign();
48129         
48130         var id = Roo.id();
48131
48132         var cfg = {
48133             cls: 'form-group',
48134             cn: []
48135         };
48136
48137         var input =  {
48138             tag: 'input',
48139             id : id,
48140             cls : 'form-control roo-money-amount-input',
48141             autocomplete: 'new-password'
48142         };
48143         
48144         var hiddenInput = {
48145             tag: 'input',
48146             type: 'hidden',
48147             id: Roo.id(),
48148             cls: 'hidden-number-input'
48149         };
48150         
48151         if(this.max_length) {
48152             input.maxlength = this.max_length; 
48153         }
48154         
48155         if (this.name) {
48156             hiddenInput.name = this.name;
48157         }
48158
48159         if (this.disabled) {
48160             input.disabled = true;
48161         }
48162
48163         var clg = 12 - this.inputlg;
48164         var cmd = 12 - this.inputmd;
48165         var csm = 12 - this.inputsm;
48166         var cxs = 12 - this.inputxs;
48167         
48168         var container = {
48169             tag : 'div',
48170             cls : 'row roo-money-field',
48171             cn : [
48172                 {
48173                     tag : 'div',
48174                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48175                     cn : [
48176                         {
48177                             tag : 'div',
48178                             cls: 'roo-select2-container input-group',
48179                             cn: [
48180                                 {
48181                                     tag : 'input',
48182                                     cls : 'form-control roo-money-currency-input',
48183                                     autocomplete: 'new-password',
48184                                     readOnly : 1,
48185                                     name : this.currencyName
48186                                 },
48187                                 {
48188                                     tag :'span',
48189                                     cls : 'input-group-addon',
48190                                     cn : [
48191                                         {
48192                                             tag: 'span',
48193                                             cls: 'caret'
48194                                         }
48195                                     ]
48196                                 }
48197                             ]
48198                         }
48199                     ]
48200                 },
48201                 {
48202                     tag : 'div',
48203                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48204                     cn : [
48205                         {
48206                             tag: 'div',
48207                             cls: this.hasFeedback ? 'has-feedback' : '',
48208                             cn: [
48209                                 input
48210                             ]
48211                         }
48212                     ]
48213                 }
48214             ]
48215             
48216         };
48217         
48218         if (this.fieldLabel.length) {
48219             var indicator = {
48220                 tag: 'i',
48221                 tooltip: 'This field is required'
48222             };
48223
48224             var label = {
48225                 tag: 'label',
48226                 'for':  id,
48227                 cls: 'control-label',
48228                 cn: []
48229             };
48230
48231             var label_text = {
48232                 tag: 'span',
48233                 html: this.fieldLabel
48234             };
48235
48236             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48237             label.cn = [
48238                 indicator,
48239                 label_text
48240             ];
48241
48242             if(this.indicatorpos == 'right') {
48243                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48244                 label.cn = [
48245                     label_text,
48246                     indicator
48247                 ];
48248             }
48249
48250             if(align == 'left') {
48251                 container = {
48252                     tag: 'div',
48253                     cn: [
48254                         container
48255                     ]
48256                 };
48257
48258                 if(this.labelWidth > 12){
48259                     label.style = "width: " + this.labelWidth + 'px';
48260                 }
48261                 if(this.labelWidth < 13 && this.labelmd == 0){
48262                     this.labelmd = this.labelWidth;
48263                 }
48264                 if(this.labellg > 0){
48265                     label.cls += ' col-lg-' + this.labellg;
48266                     input.cls += ' col-lg-' + (12 - this.labellg);
48267                 }
48268                 if(this.labelmd > 0){
48269                     label.cls += ' col-md-' + this.labelmd;
48270                     container.cls += ' col-md-' + (12 - this.labelmd);
48271                 }
48272                 if(this.labelsm > 0){
48273                     label.cls += ' col-sm-' + this.labelsm;
48274                     container.cls += ' col-sm-' + (12 - this.labelsm);
48275                 }
48276                 if(this.labelxs > 0){
48277                     label.cls += ' col-xs-' + this.labelxs;
48278                     container.cls += ' col-xs-' + (12 - this.labelxs);
48279                 }
48280             }
48281         }
48282
48283         cfg.cn = [
48284             label,
48285             container,
48286             hiddenInput
48287         ];
48288         
48289         var settings = this;
48290
48291         ['xs','sm','md','lg'].map(function(size){
48292             if (settings[size]) {
48293                 cfg.cls += ' col-' + size + '-' + settings[size];
48294             }
48295         });
48296         
48297         return cfg;
48298     },
48299     
48300     initEvents : function()
48301     {
48302         this.indicator = this.indicatorEl();
48303         
48304         this.initCurrencyEvent();
48305         
48306         this.initNumberEvent();
48307     },
48308     
48309     initCurrencyEvent : function()
48310     {
48311         if (!this.store) {
48312             throw "can not find store for combo";
48313         }
48314         
48315         this.store = Roo.factory(this.store, Roo.data);
48316         this.store.parent = this;
48317         
48318         this.createList();
48319         
48320         this.triggerEl = this.el.select('.input-group-addon', true).first();
48321         
48322         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48323         
48324         var _this = this;
48325         
48326         (function(){
48327             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48328             _this.list.setWidth(lw);
48329         }).defer(100);
48330         
48331         this.list.on('mouseover', this.onViewOver, this);
48332         this.list.on('mousemove', this.onViewMove, this);
48333         this.list.on('scroll', this.onViewScroll, this);
48334         
48335         if(!this.tpl){
48336             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48337         }
48338         
48339         this.view = new Roo.View(this.list, this.tpl, {
48340             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48341         });
48342         
48343         this.view.on('click', this.onViewClick, this);
48344         
48345         this.store.on('beforeload', this.onBeforeLoad, this);
48346         this.store.on('load', this.onLoad, this);
48347         this.store.on('loadexception', this.onLoadException, this);
48348         
48349         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48350             "up" : function(e){
48351                 this.inKeyMode = true;
48352                 this.selectPrev();
48353             },
48354
48355             "down" : function(e){
48356                 if(!this.isExpanded()){
48357                     this.onTriggerClick();
48358                 }else{
48359                     this.inKeyMode = true;
48360                     this.selectNext();
48361                 }
48362             },
48363
48364             "enter" : function(e){
48365                 this.collapse();
48366                 
48367                 if(this.fireEvent("specialkey", this, e)){
48368                     this.onViewClick(false);
48369                 }
48370                 
48371                 return true;
48372             },
48373
48374             "esc" : function(e){
48375                 this.collapse();
48376             },
48377
48378             "tab" : function(e){
48379                 this.collapse();
48380                 
48381                 if(this.fireEvent("specialkey", this, e)){
48382                     this.onViewClick(false);
48383                 }
48384                 
48385                 return true;
48386             },
48387
48388             scope : this,
48389
48390             doRelay : function(foo, bar, hname){
48391                 if(hname == 'down' || this.scope.isExpanded()){
48392                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48393                 }
48394                 return true;
48395             },
48396
48397             forceKeyDown: true
48398         });
48399         
48400         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48401         
48402     },
48403     
48404     initNumberEvent : function(e)
48405     {
48406         this.inputEl().on("keydown" , this.fireKey,  this);
48407         this.inputEl().on("focus", this.onFocus,  this);
48408         this.inputEl().on("blur", this.onBlur,  this);
48409         
48410         this.inputEl().relayEvent('keyup', this);
48411         
48412         if(this.indicator){
48413             this.indicator.addClass('invisible');
48414         }
48415  
48416         this.originalValue = this.getValue();
48417         
48418         if(this.validationEvent == 'keyup'){
48419             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48420             this.inputEl().on('keyup', this.filterValidation, this);
48421         }
48422         else if(this.validationEvent !== false){
48423             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48424         }
48425         
48426         if(this.selectOnFocus){
48427             this.on("focus", this.preFocus, this);
48428             
48429         }
48430         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48431             this.inputEl().on("keypress", this.filterKeys, this);
48432         } else {
48433             this.inputEl().relayEvent('keypress', this);
48434         }
48435         
48436         var allowed = "0123456789";
48437         
48438         if(this.allowDecimals){
48439             allowed += this.decimalSeparator;
48440         }
48441         
48442         if(this.allowNegative){
48443             allowed += "-";
48444         }
48445         
48446         if(this.thousandsDelimiter) {
48447             allowed += ",";
48448         }
48449         
48450         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48451         
48452         var keyPress = function(e){
48453             
48454             var k = e.getKey();
48455             
48456             var c = e.getCharCode();
48457             
48458             if(
48459                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48460                     allowed.indexOf(String.fromCharCode(c)) === -1
48461             ){
48462                 e.stopEvent();
48463                 return;
48464             }
48465             
48466             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48467                 return;
48468             }
48469             
48470             if(allowed.indexOf(String.fromCharCode(c)) === -1){
48471                 e.stopEvent();
48472             }
48473         };
48474         
48475         this.inputEl().on("keypress", keyPress, this);
48476         
48477     },
48478     
48479     onTriggerClick : function(e)
48480     {   
48481         if(this.disabled){
48482             return;
48483         }
48484         
48485         this.page = 0;
48486         this.loadNext = false;
48487         
48488         if(this.isExpanded()){
48489             this.collapse();
48490             return;
48491         }
48492         
48493         this.hasFocus = true;
48494         
48495         if(this.triggerAction == 'all') {
48496             this.doQuery(this.allQuery, true);
48497             return;
48498         }
48499         
48500         this.doQuery(this.getRawValue());
48501     },
48502     
48503     getCurrency : function()
48504     {   
48505         var v = this.currencyEl().getValue();
48506         
48507         return v;
48508     },
48509     
48510     restrictHeight : function()
48511     {
48512         this.list.alignTo(this.currencyEl(), this.listAlign);
48513         this.list.alignTo(this.currencyEl(), this.listAlign);
48514     },
48515     
48516     onViewClick : function(view, doFocus, el, e)
48517     {
48518         var index = this.view.getSelectedIndexes()[0];
48519         
48520         var r = this.store.getAt(index);
48521         
48522         if(r){
48523             this.onSelect(r, index);
48524         }
48525     },
48526     
48527     onSelect : function(record, index){
48528         
48529         if(this.fireEvent('beforeselect', this, record, index) !== false){
48530         
48531             this.setFromCurrencyData(index > -1 ? record.data : false);
48532             
48533             this.collapse();
48534             
48535             this.fireEvent('select', this, record, index);
48536         }
48537     },
48538     
48539     setFromCurrencyData : function(o)
48540     {
48541         var currency = '';
48542         
48543         this.lastCurrency = o;
48544         
48545         if (this.currencyField) {
48546             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
48547         } else {
48548             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
48549         }
48550         
48551         this.lastSelectionText = currency;
48552         
48553         //setting default currency
48554         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
48555             this.setCurrency(this.defaultCurrency);
48556             return;
48557         }
48558         
48559         this.setCurrency(currency);
48560     },
48561     
48562     setFromData : function(o)
48563     {
48564         var c = {};
48565         
48566         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
48567         
48568         this.setFromCurrencyData(c);
48569         
48570         var value = '';
48571         
48572         if (this.name) {
48573             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
48574         } else {
48575             Roo.log('no value set for '+ (this.name ? this.name : this.id));
48576         }
48577         
48578         this.setValue(value);
48579         
48580     },
48581     
48582     setCurrency : function(v)
48583     {   
48584         this.currencyValue = v;
48585         
48586         if(this.rendered){
48587             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
48588             this.validate();
48589         }
48590     },
48591     
48592     setValue : function(v)
48593     {
48594         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
48595         
48596         this.value = v;
48597         
48598         if(this.rendered){
48599             
48600             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48601             
48602             this.inputEl().dom.value = (v == '') ? '' :
48603                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
48604             
48605             if(!this.allowZero && v === '0') {
48606                 this.hiddenEl().dom.value = '';
48607                 this.inputEl().dom.value = '';
48608             }
48609             
48610             this.validate();
48611         }
48612     },
48613     
48614     getRawValue : function()
48615     {
48616         var v = this.inputEl().getValue();
48617         
48618         return v;
48619     },
48620     
48621     getValue : function()
48622     {
48623         return this.fixPrecision(this.parseValue(this.getRawValue()));
48624     },
48625     
48626     parseValue : function(value)
48627     {
48628         if(this.thousandsDelimiter) {
48629             value += "";
48630             r = new RegExp(",", "g");
48631             value = value.replace(r, "");
48632         }
48633         
48634         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
48635         return isNaN(value) ? '' : value;
48636         
48637     },
48638     
48639     fixPrecision : function(value)
48640     {
48641         if(this.thousandsDelimiter) {
48642             value += "";
48643             r = new RegExp(",", "g");
48644             value = value.replace(r, "");
48645         }
48646         
48647         var nan = isNaN(value);
48648         
48649         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
48650             return nan ? '' : value;
48651         }
48652         return parseFloat(value).toFixed(this.decimalPrecision);
48653     },
48654     
48655     decimalPrecisionFcn : function(v)
48656     {
48657         return Math.floor(v);
48658     },
48659     
48660     validateValue : function(value)
48661     {
48662         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
48663             return false;
48664         }
48665         
48666         var num = this.parseValue(value);
48667         
48668         if(isNaN(num)){
48669             this.markInvalid(String.format(this.nanText, value));
48670             return false;
48671         }
48672         
48673         if(num < this.minValue){
48674             this.markInvalid(String.format(this.minText, this.minValue));
48675             return false;
48676         }
48677         
48678         if(num > this.maxValue){
48679             this.markInvalid(String.format(this.maxText, this.maxValue));
48680             return false;
48681         }
48682         
48683         return true;
48684     },
48685     
48686     validate : function()
48687     {
48688         if(this.disabled || this.allowBlank){
48689             this.markValid();
48690             return true;
48691         }
48692         
48693         var currency = this.getCurrency();
48694         
48695         if(this.validateValue(this.getRawValue()) && currency.length){
48696             this.markValid();
48697             return true;
48698         }
48699         
48700         this.markInvalid();
48701         return false;
48702     },
48703     
48704     getName: function()
48705     {
48706         return this.name;
48707     },
48708     
48709     beforeBlur : function()
48710     {
48711         if(!this.castInt){
48712             return;
48713         }
48714         
48715         var v = this.parseValue(this.getRawValue());
48716         
48717         if(v || v == 0){
48718             this.setValue(v);
48719         }
48720     },
48721     
48722     onBlur : function()
48723     {
48724         this.beforeBlur();
48725         
48726         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
48727             //this.el.removeClass(this.focusClass);
48728         }
48729         
48730         this.hasFocus = false;
48731         
48732         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
48733             this.validate();
48734         }
48735         
48736         var v = this.getValue();
48737         
48738         if(String(v) !== String(this.startValue)){
48739             this.fireEvent('change', this, v, this.startValue);
48740         }
48741         
48742         this.fireEvent("blur", this);
48743     },
48744     
48745     inputEl : function()
48746     {
48747         return this.el.select('.roo-money-amount-input', true).first();
48748     },
48749     
48750     currencyEl : function()
48751     {
48752         return this.el.select('.roo-money-currency-input', true).first();
48753     },
48754     
48755     hiddenEl : function()
48756     {
48757         return this.el.select('input.hidden-number-input',true).first();
48758     }
48759     
48760 });/**
48761  * @class Roo.bootstrap.BezierSignature
48762  * @extends Roo.bootstrap.Component
48763  * Bootstrap BezierSignature class
48764  * This script refer to:
48765  *    Title: Signature Pad
48766  *    Author: szimek
48767  *    Availability: https://github.com/szimek/signature_pad
48768  *
48769  * @constructor
48770  * Create a new BezierSignature
48771  * @param {Object} config The config object
48772  */
48773
48774 Roo.bootstrap.BezierSignature = function(config){
48775     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
48776     this.addEvents({
48777         "resize" : true
48778     });
48779 };
48780
48781 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
48782 {
48783      
48784     curve_data: [],
48785     
48786     is_empty: true,
48787     
48788     mouse_btn_down: true,
48789     
48790     /**
48791      * @cfg {int} canvas height
48792      */
48793     canvas_height: '200px',
48794     
48795     /**
48796      * @cfg {float|function} Radius of a single dot.
48797      */ 
48798     dot_size: false,
48799     
48800     /**
48801      * @cfg {float} Minimum width of a line. Defaults to 0.5.
48802      */
48803     min_width: 0.5,
48804     
48805     /**
48806      * @cfg {float} Maximum width of a line. Defaults to 2.5.
48807      */
48808     max_width: 2.5,
48809     
48810     /**
48811      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
48812      */
48813     throttle: 16,
48814     
48815     /**
48816      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
48817      */
48818     min_distance: 5,
48819     
48820     /**
48821      * @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.
48822      */
48823     bg_color: 'rgba(0, 0, 0, 0)',
48824     
48825     /**
48826      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
48827      */
48828     dot_color: 'black',
48829     
48830     /**
48831      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
48832      */ 
48833     velocity_filter_weight: 0.7,
48834     
48835     /**
48836      * @cfg {function} Callback when stroke begin. 
48837      */
48838     onBegin: false,
48839     
48840     /**
48841      * @cfg {function} Callback when stroke end.
48842      */
48843     onEnd: false,
48844     
48845     getAutoCreate : function()
48846     {
48847         var cls = 'roo-signature column';
48848         
48849         if(this.cls){
48850             cls += ' ' + this.cls;
48851         }
48852         
48853         var col_sizes = [
48854             'lg',
48855             'md',
48856             'sm',
48857             'xs'
48858         ];
48859         
48860         for(var i = 0; i < col_sizes.length; i++) {
48861             if(this[col_sizes[i]]) {
48862                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
48863             }
48864         }
48865         
48866         var cfg = {
48867             tag: 'div',
48868             cls: cls,
48869             cn: [
48870                 {
48871                     tag: 'div',
48872                     cls: 'roo-signature-body',
48873                     cn: [
48874                         {
48875                             tag: 'canvas',
48876                             cls: 'roo-signature-body-canvas',
48877                             height: this.canvas_height,
48878                             width: this.canvas_width
48879                         }
48880                     ]
48881                 },
48882                 {
48883                     tag: 'input',
48884                     type: 'file',
48885                     style: 'display: none'
48886                 }
48887             ]
48888         };
48889         
48890         return cfg;
48891     },
48892     
48893     initEvents: function() 
48894     {
48895         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
48896         
48897         var canvas = this.canvasEl();
48898         
48899         // mouse && touch event swapping...
48900         canvas.dom.style.touchAction = 'none';
48901         canvas.dom.style.msTouchAction = 'none';
48902         
48903         this.mouse_btn_down = false;
48904         canvas.on('mousedown', this._handleMouseDown, this);
48905         canvas.on('mousemove', this._handleMouseMove, this);
48906         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
48907         
48908         if (window.PointerEvent) {
48909             canvas.on('pointerdown', this._handleMouseDown, this);
48910             canvas.on('pointermove', this._handleMouseMove, this);
48911             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
48912         }
48913         
48914         if ('ontouchstart' in window) {
48915             canvas.on('touchstart', this._handleTouchStart, this);
48916             canvas.on('touchmove', this._handleTouchMove, this);
48917             canvas.on('touchend', this._handleTouchEnd, this);
48918         }
48919         
48920         Roo.EventManager.onWindowResize(this.resize, this, true);
48921         
48922         // file input event
48923         this.fileEl().on('change', this.uploadImage, this);
48924         
48925         this.clear();
48926         
48927         this.resize();
48928     },
48929     
48930     resize: function(){
48931         
48932         var canvas = this.canvasEl().dom;
48933         var ctx = this.canvasElCtx();
48934         var img_data = false;
48935         
48936         if(canvas.width > 0) {
48937             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
48938         }
48939         // setting canvas width will clean img data
48940         canvas.width = 0;
48941         
48942         var style = window.getComputedStyle ? 
48943             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
48944             
48945         var padding_left = parseInt(style.paddingLeft) || 0;
48946         var padding_right = parseInt(style.paddingRight) || 0;
48947         
48948         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
48949         
48950         if(img_data) {
48951             ctx.putImageData(img_data, 0, 0);
48952         }
48953     },
48954     
48955     _handleMouseDown: function(e)
48956     {
48957         if (e.browserEvent.which === 1) {
48958             this.mouse_btn_down = true;
48959             this.strokeBegin(e);
48960         }
48961     },
48962     
48963     _handleMouseMove: function (e)
48964     {
48965         if (this.mouse_btn_down) {
48966             this.strokeMoveUpdate(e);
48967         }
48968     },
48969     
48970     _handleMouseUp: function (e)
48971     {
48972         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
48973             this.mouse_btn_down = false;
48974             this.strokeEnd(e);
48975         }
48976     },
48977     
48978     _handleTouchStart: function (e) {
48979         
48980         e.preventDefault();
48981         if (e.browserEvent.targetTouches.length === 1) {
48982             // var touch = e.browserEvent.changedTouches[0];
48983             // this.strokeBegin(touch);
48984             
48985              this.strokeBegin(e); // assume e catching the correct xy...
48986         }
48987     },
48988     
48989     _handleTouchMove: function (e) {
48990         e.preventDefault();
48991         // var touch = event.targetTouches[0];
48992         // _this._strokeMoveUpdate(touch);
48993         this.strokeMoveUpdate(e);
48994     },
48995     
48996     _handleTouchEnd: function (e) {
48997         var wasCanvasTouched = e.target === this.canvasEl().dom;
48998         if (wasCanvasTouched) {
48999             e.preventDefault();
49000             // var touch = event.changedTouches[0];
49001             // _this._strokeEnd(touch);
49002             this.strokeEnd(e);
49003         }
49004     },
49005     
49006     reset: function () {
49007         this._lastPoints = [];
49008         this._lastVelocity = 0;
49009         this._lastWidth = (this.min_width + this.max_width) / 2;
49010         this.canvasElCtx().fillStyle = this.dot_color;
49011     },
49012     
49013     strokeMoveUpdate: function(e)
49014     {
49015         this.strokeUpdate(e);
49016         
49017         if (this.throttle) {
49018             this.throttleStroke(this.strokeUpdate, this.throttle);
49019         }
49020         else {
49021             this.strokeUpdate(e);
49022         }
49023     },
49024     
49025     strokeBegin: function(e)
49026     {
49027         var newPointGroup = {
49028             color: this.dot_color,
49029             points: []
49030         };
49031         
49032         if (typeof this.onBegin === 'function') {
49033             this.onBegin(e);
49034         }
49035         
49036         this.curve_data.push(newPointGroup);
49037         this.reset();
49038         this.strokeUpdate(e);
49039     },
49040     
49041     strokeUpdate: function(e)
49042     {
49043         var rect = this.canvasEl().dom.getBoundingClientRect();
49044         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49045         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49046         var lastPoints = lastPointGroup.points;
49047         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49048         var isLastPointTooClose = lastPoint
49049             ? point.distanceTo(lastPoint) <= this.min_distance
49050             : false;
49051         var color = lastPointGroup.color;
49052         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49053             var curve = this.addPoint(point);
49054             if (!lastPoint) {
49055                 this.drawDot({color: color, point: point});
49056             }
49057             else if (curve) {
49058                 this.drawCurve({color: color, curve: curve});
49059             }
49060             lastPoints.push({
49061                 time: point.time,
49062                 x: point.x,
49063                 y: point.y
49064             });
49065         }
49066     },
49067     
49068     strokeEnd: function(e)
49069     {
49070         this.strokeUpdate(e);
49071         if (typeof this.onEnd === 'function') {
49072             this.onEnd(e);
49073         }
49074     },
49075     
49076     addPoint:  function (point) {
49077         var _lastPoints = this._lastPoints;
49078         _lastPoints.push(point);
49079         if (_lastPoints.length > 2) {
49080             if (_lastPoints.length === 3) {
49081                 _lastPoints.unshift(_lastPoints[0]);
49082             }
49083             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49084             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49085             _lastPoints.shift();
49086             return curve;
49087         }
49088         return null;
49089     },
49090     
49091     calculateCurveWidths: function (startPoint, endPoint) {
49092         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49093             (1 - this.velocity_filter_weight) * this._lastVelocity;
49094
49095         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49096         var widths = {
49097             end: newWidth,
49098             start: this._lastWidth
49099         };
49100         
49101         this._lastVelocity = velocity;
49102         this._lastWidth = newWidth;
49103         return widths;
49104     },
49105     
49106     drawDot: function (_a) {
49107         var color = _a.color, point = _a.point;
49108         var ctx = this.canvasElCtx();
49109         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49110         ctx.beginPath();
49111         this.drawCurveSegment(point.x, point.y, width);
49112         ctx.closePath();
49113         ctx.fillStyle = color;
49114         ctx.fill();
49115     },
49116     
49117     drawCurve: function (_a) {
49118         var color = _a.color, curve = _a.curve;
49119         var ctx = this.canvasElCtx();
49120         var widthDelta = curve.endWidth - curve.startWidth;
49121         var drawSteps = Math.floor(curve.length()) * 2;
49122         ctx.beginPath();
49123         ctx.fillStyle = color;
49124         for (var i = 0; i < drawSteps; i += 1) {
49125         var t = i / drawSteps;
49126         var tt = t * t;
49127         var ttt = tt * t;
49128         var u = 1 - t;
49129         var uu = u * u;
49130         var uuu = uu * u;
49131         var x = uuu * curve.startPoint.x;
49132         x += 3 * uu * t * curve.control1.x;
49133         x += 3 * u * tt * curve.control2.x;
49134         x += ttt * curve.endPoint.x;
49135         var y = uuu * curve.startPoint.y;
49136         y += 3 * uu * t * curve.control1.y;
49137         y += 3 * u * tt * curve.control2.y;
49138         y += ttt * curve.endPoint.y;
49139         var width = curve.startWidth + ttt * widthDelta;
49140         this.drawCurveSegment(x, y, width);
49141         }
49142         ctx.closePath();
49143         ctx.fill();
49144     },
49145     
49146     drawCurveSegment: function (x, y, width) {
49147         var ctx = this.canvasElCtx();
49148         ctx.moveTo(x, y);
49149         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49150         this.is_empty = false;
49151     },
49152     
49153     clear: function()
49154     {
49155         var ctx = this.canvasElCtx();
49156         var canvas = this.canvasEl().dom;
49157         ctx.fillStyle = this.bg_color;
49158         ctx.clearRect(0, 0, canvas.width, canvas.height);
49159         ctx.fillRect(0, 0, canvas.width, canvas.height);
49160         this.curve_data = [];
49161         this.reset();
49162         this.is_empty = true;
49163     },
49164     
49165     fileEl: function()
49166     {
49167         return  this.el.select('input',true).first();
49168     },
49169     
49170     canvasEl: function()
49171     {
49172         return this.el.select('canvas',true).first();
49173     },
49174     
49175     canvasElCtx: function()
49176     {
49177         return this.el.select('canvas',true).first().dom.getContext('2d');
49178     },
49179     
49180     getImage: function(type)
49181     {
49182         if(this.is_empty) {
49183             return false;
49184         }
49185         
49186         // encryption ?
49187         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49188     },
49189     
49190     drawFromImage: function(img_src)
49191     {
49192         var img = new Image();
49193         
49194         img.onload = function(){
49195             this.canvasElCtx().drawImage(img, 0, 0);
49196         }.bind(this);
49197         
49198         img.src = img_src;
49199         
49200         this.is_empty = false;
49201     },
49202     
49203     selectImage: function()
49204     {
49205         this.fileEl().dom.click();
49206     },
49207     
49208     uploadImage: function(e)
49209     {
49210         var reader = new FileReader();
49211         
49212         reader.onload = function(e){
49213             var img = new Image();
49214             img.onload = function(){
49215                 this.reset();
49216                 this.canvasElCtx().drawImage(img, 0, 0);
49217             }.bind(this);
49218             img.src = e.target.result;
49219         }.bind(this);
49220         
49221         reader.readAsDataURL(e.target.files[0]);
49222     },
49223     
49224     // Bezier Point Constructor
49225     Point: (function () {
49226         function Point(x, y, time) {
49227             this.x = x;
49228             this.y = y;
49229             this.time = time || Date.now();
49230         }
49231         Point.prototype.distanceTo = function (start) {
49232             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49233         };
49234         Point.prototype.equals = function (other) {
49235             return this.x === other.x && this.y === other.y && this.time === other.time;
49236         };
49237         Point.prototype.velocityFrom = function (start) {
49238             return this.time !== start.time
49239             ? this.distanceTo(start) / (this.time - start.time)
49240             : 0;
49241         };
49242         return Point;
49243     }()),
49244     
49245     
49246     // Bezier Constructor
49247     Bezier: (function () {
49248         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49249             this.startPoint = startPoint;
49250             this.control2 = control2;
49251             this.control1 = control1;
49252             this.endPoint = endPoint;
49253             this.startWidth = startWidth;
49254             this.endWidth = endWidth;
49255         }
49256         Bezier.fromPoints = function (points, widths, scope) {
49257             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49258             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49259             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49260         };
49261         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49262             var dx1 = s1.x - s2.x;
49263             var dy1 = s1.y - s2.y;
49264             var dx2 = s2.x - s3.x;
49265             var dy2 = s2.y - s3.y;
49266             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49267             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49268             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49269             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49270             var dxm = m1.x - m2.x;
49271             var dym = m1.y - m2.y;
49272             var k = l2 / (l1 + l2);
49273             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49274             var tx = s2.x - cm.x;
49275             var ty = s2.y - cm.y;
49276             return {
49277                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49278                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49279             };
49280         };
49281         Bezier.prototype.length = function () {
49282             var steps = 10;
49283             var length = 0;
49284             var px;
49285             var py;
49286             for (var i = 0; i <= steps; i += 1) {
49287                 var t = i / steps;
49288                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49289                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49290                 if (i > 0) {
49291                     var xdiff = cx - px;
49292                     var ydiff = cy - py;
49293                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49294                 }
49295                 px = cx;
49296                 py = cy;
49297             }
49298             return length;
49299         };
49300         Bezier.prototype.point = function (t, start, c1, c2, end) {
49301             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49302             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49303             + (3.0 * c2 * (1.0 - t) * t * t)
49304             + (end * t * t * t);
49305         };
49306         return Bezier;
49307     }()),
49308     
49309     throttleStroke: function(fn, wait) {
49310       if (wait === void 0) { wait = 250; }
49311       var previous = 0;
49312       var timeout = null;
49313       var result;
49314       var storedContext;
49315       var storedArgs;
49316       var later = function () {
49317           previous = Date.now();
49318           timeout = null;
49319           result = fn.apply(storedContext, storedArgs);
49320           if (!timeout) {
49321               storedContext = null;
49322               storedArgs = [];
49323           }
49324       };
49325       return function wrapper() {
49326           var args = [];
49327           for (var _i = 0; _i < arguments.length; _i++) {
49328               args[_i] = arguments[_i];
49329           }
49330           var now = Date.now();
49331           var remaining = wait - (now - previous);
49332           storedContext = this;
49333           storedArgs = args;
49334           if (remaining <= 0 || remaining > wait) {
49335               if (timeout) {
49336                   clearTimeout(timeout);
49337                   timeout = null;
49338               }
49339               previous = now;
49340               result = fn.apply(storedContext, storedArgs);
49341               if (!timeout) {
49342                   storedContext = null;
49343                   storedArgs = [];
49344               }
49345           }
49346           else if (!timeout) {
49347               timeout = window.setTimeout(later, remaining);
49348           }
49349           return result;
49350       };
49351   }
49352   
49353 });
49354
49355  
49356
49357  // old names for form elements
49358 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49359 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49360 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49361 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49362 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49363 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49364 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49365 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49366 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49367 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49368 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49369 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49370 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49371 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49372 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49373 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49374 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49375 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49376 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49377 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49378 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49379 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49380 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49381 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49382 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49383 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49384
49385 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49386 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49387
49388 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49389 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49390
49391 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49392 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49393 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49394 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49395