8f4f9b513b33cda645198a2aa537e5b8b99d4167
[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             var rt = response.responseText;
11288             if (rt.match(/^\<!--\[CDATA\[/)) {
11289                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11290                 rt = rt.replace(/\]\]--\>$/,'');
11291             }
11292             
11293             ret = Roo.decode(rt);
11294         } catch (e) {
11295             ret = {
11296                 success: false,
11297                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11298                 errors : []
11299             };
11300         }
11301         return ret;
11302         
11303     }
11304 });
11305
11306
11307 Roo.form.Action.Load = function(form, options){
11308     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11309     this.reader = this.form.reader;
11310 };
11311
11312 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11313     type : 'load',
11314
11315     run : function(){
11316         
11317         Roo.Ajax.request(Roo.apply(
11318                 this.createCallback(), {
11319                     method:this.getMethod(),
11320                     url:this.getUrl(false),
11321                     params:this.getParams()
11322         }));
11323     },
11324
11325     success : function(response){
11326         
11327         var result = this.processResponse(response);
11328         if(result === true || !result.success || !result.data){
11329             this.failureType = Roo.form.Action.LOAD_FAILURE;
11330             this.form.afterAction(this, false);
11331             return;
11332         }
11333         this.form.clearInvalid();
11334         this.form.setValues(result.data);
11335         this.form.afterAction(this, true);
11336     },
11337
11338     handleResponse : function(response){
11339         if(this.form.reader){
11340             var rs = this.form.reader.read(response);
11341             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11342             return {
11343                 success : rs.success,
11344                 data : data
11345             };
11346         }
11347         return Roo.decode(response.responseText);
11348     }
11349 });
11350
11351 Roo.form.Action.ACTION_TYPES = {
11352     'load' : Roo.form.Action.Load,
11353     'submit' : Roo.form.Action.Submit
11354 };/*
11355  * - LGPL
11356  *
11357  * form
11358  *
11359  */
11360
11361 /**
11362  * @class Roo.bootstrap.form.Form
11363  * @extends Roo.bootstrap.Component
11364  * @children Roo.bootstrap.Component
11365  * Bootstrap Form class
11366  * @cfg {String} method  GET | POST (default POST)
11367  * @cfg {String} labelAlign top | left (default top)
11368  * @cfg {String} align left  | right - for navbars
11369  * @cfg {Boolean} loadMask load mask when submit (default true)
11370
11371  *
11372  * @constructor
11373  * Create a new Form
11374  * @param {Object} config The config object
11375  */
11376
11377
11378 Roo.bootstrap.form.Form = function(config){
11379     
11380     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11381     
11382     Roo.bootstrap.form.Form.popover.apply();
11383     
11384     this.addEvents({
11385         /**
11386          * @event clientvalidation
11387          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11388          * @param {Form} this
11389          * @param {Boolean} valid true if the form has passed client-side validation
11390          */
11391         clientvalidation: true,
11392         /**
11393          * @event beforeaction
11394          * Fires before any action is performed. Return false to cancel the action.
11395          * @param {Form} this
11396          * @param {Action} action The action to be performed
11397          */
11398         beforeaction: true,
11399         /**
11400          * @event actionfailed
11401          * Fires when an action fails.
11402          * @param {Form} this
11403          * @param {Action} action The action that failed
11404          */
11405         actionfailed : true,
11406         /**
11407          * @event actioncomplete
11408          * Fires when an action is completed.
11409          * @param {Form} this
11410          * @param {Action} action The action that completed
11411          */
11412         actioncomplete : true
11413     });
11414 };
11415
11416 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11417
11418      /**
11419      * @cfg {String} method
11420      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11421      */
11422     method : 'POST',
11423     /**
11424      * @cfg {String} url
11425      * The URL to use for form actions if one isn't supplied in the action options.
11426      */
11427     /**
11428      * @cfg {Boolean} fileUpload
11429      * Set to true if this form is a file upload.
11430      */
11431
11432     /**
11433      * @cfg {Object} baseParams
11434      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11435      */
11436
11437     /**
11438      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11439      */
11440     timeout: 30,
11441     /**
11442      * @cfg {Sting} align (left|right) for navbar forms
11443      */
11444     align : 'left',
11445
11446     // private
11447     activeAction : null,
11448
11449     /**
11450      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11451      * element by passing it or its id or mask the form itself by passing in true.
11452      * @type Mixed
11453      */
11454     waitMsgTarget : false,
11455
11456     loadMask : true,
11457     
11458     /**
11459      * @cfg {Boolean} errorMask (true|false) default false
11460      */
11461     errorMask : false,
11462     
11463     /**
11464      * @cfg {Number} maskOffset Default 100
11465      */
11466     maskOffset : 100,
11467     
11468     /**
11469      * @cfg {Boolean} maskBody
11470      */
11471     maskBody : false,
11472
11473     getAutoCreate : function(){
11474
11475         var cfg = {
11476             tag: 'form',
11477             method : this.method || 'POST',
11478             id : this.id || Roo.id(),
11479             cls : ''
11480         };
11481         if (this.parent().xtype.match(/^Nav/)) {
11482             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11483
11484         }
11485
11486         if (this.labelAlign == 'left' ) {
11487             cfg.cls += ' form-horizontal';
11488         }
11489
11490
11491         return cfg;
11492     },
11493     initEvents : function()
11494     {
11495         this.el.on('submit', this.onSubmit, this);
11496         // this was added as random key presses on the form where triggering form submit.
11497         this.el.on('keypress', function(e) {
11498             if (e.getCharCode() != 13) {
11499                 return true;
11500             }
11501             // we might need to allow it for textareas.. and some other items.
11502             // check e.getTarget().
11503
11504             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11505                 return true;
11506             }
11507
11508             Roo.log("keypress blocked");
11509
11510             e.preventDefault();
11511             return false;
11512         });
11513         
11514     },
11515     // private
11516     onSubmit : function(e){
11517         e.stopEvent();
11518     },
11519
11520      /**
11521      * Returns true if client-side validation on the form is successful.
11522      * @return Boolean
11523      */
11524     isValid : function(){
11525         var items = this.getItems();
11526         var valid = true;
11527         var target = false;
11528         
11529         items.each(function(f){
11530             
11531             if(f.validate()){
11532                 return;
11533             }
11534             
11535             Roo.log('invalid field: ' + f.name);
11536             
11537             valid = false;
11538
11539             if(!target && f.el.isVisible(true)){
11540                 target = f;
11541             }
11542            
11543         });
11544         
11545         if(this.errorMask && !valid){
11546             Roo.bootstrap.form.Form.popover.mask(this, target);
11547         }
11548         
11549         return valid;
11550     },
11551     
11552     /**
11553      * Returns true if any fields in this form have changed since their original load.
11554      * @return Boolean
11555      */
11556     isDirty : function(){
11557         var dirty = false;
11558         var items = this.getItems();
11559         items.each(function(f){
11560            if(f.isDirty()){
11561                dirty = true;
11562                return false;
11563            }
11564            return true;
11565         });
11566         return dirty;
11567     },
11568      /**
11569      * Performs a predefined action (submit or load) or custom actions you define on this form.
11570      * @param {String} actionName The name of the action type
11571      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11572      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11573      * accept other config options):
11574      * <pre>
11575 Property          Type             Description
11576 ----------------  ---------------  ----------------------------------------------------------------------------------
11577 url               String           The url for the action (defaults to the form's url)
11578 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11579 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11580 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11581                                    validate the form on the client (defaults to false)
11582      * </pre>
11583      * @return {BasicForm} this
11584      */
11585     doAction : function(action, options){
11586         if(typeof action == 'string'){
11587             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11588         }
11589         if(this.fireEvent('beforeaction', this, action) !== false){
11590             this.beforeAction(action);
11591             action.run.defer(100, action);
11592         }
11593         return this;
11594     },
11595
11596     // private
11597     beforeAction : function(action){
11598         var o = action.options;
11599         
11600         if(this.loadMask){
11601             
11602             if(this.maskBody){
11603                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11604             } else {
11605                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11606             }
11607         }
11608         // not really supported yet.. ??
11609
11610         //if(this.waitMsgTarget === true){
11611         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11612         //}else if(this.waitMsgTarget){
11613         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11614         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11615         //}else {
11616         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11617        // }
11618
11619     },
11620
11621     // private
11622     afterAction : function(action, success){
11623         this.activeAction = null;
11624         var o = action.options;
11625
11626         if(this.loadMask){
11627             
11628             if(this.maskBody){
11629                 Roo.get(document.body).unmask();
11630             } else {
11631                 this.el.unmask();
11632             }
11633         }
11634         
11635         //if(this.waitMsgTarget === true){
11636 //            this.el.unmask();
11637         //}else if(this.waitMsgTarget){
11638         //    this.waitMsgTarget.unmask();
11639         //}else{
11640         //    Roo.MessageBox.updateProgress(1);
11641         //    Roo.MessageBox.hide();
11642        // }
11643         //
11644         if(success){
11645             if(o.reset){
11646                 this.reset();
11647             }
11648             Roo.callback(o.success, o.scope, [this, action]);
11649             this.fireEvent('actioncomplete', this, action);
11650
11651         }else{
11652
11653             // failure condition..
11654             // we have a scenario where updates need confirming.
11655             // eg. if a locking scenario exists..
11656             // we look for { errors : { needs_confirm : true }} in the response.
11657             if (
11658                 (typeof(action.result) != 'undefined')  &&
11659                 (typeof(action.result.errors) != 'undefined')  &&
11660                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11661            ){
11662                 var _t = this;
11663                 Roo.log("not supported yet");
11664                  /*
11665
11666                 Roo.MessageBox.confirm(
11667                     "Change requires confirmation",
11668                     action.result.errorMsg,
11669                     function(r) {
11670                         if (r != 'yes') {
11671                             return;
11672                         }
11673                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11674                     }
11675
11676                 );
11677                 */
11678
11679
11680                 return;
11681             }
11682
11683             Roo.callback(o.failure, o.scope, [this, action]);
11684             // show an error message if no failed handler is set..
11685             if (!this.hasListener('actionfailed')) {
11686                 Roo.log("need to add dialog support");
11687                 /*
11688                 Roo.MessageBox.alert("Error",
11689                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11690                         action.result.errorMsg :
11691                         "Saving Failed, please check your entries or try again"
11692                 );
11693                 */
11694             }
11695
11696             this.fireEvent('actionfailed', this, action);
11697         }
11698
11699     },
11700     /**
11701      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11702      * @param {String} id The value to search for
11703      * @return Field
11704      */
11705     findField : function(id){
11706         var items = this.getItems();
11707         var field = items.get(id);
11708         if(!field){
11709              items.each(function(f){
11710                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11711                     field = f;
11712                     return false;
11713                 }
11714                 return true;
11715             });
11716         }
11717         return field || null;
11718     },
11719      /**
11720      * Mark fields in this form invalid in bulk.
11721      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11722      * @return {BasicForm} this
11723      */
11724     markInvalid : function(errors){
11725         if(errors instanceof Array){
11726             for(var i = 0, len = errors.length; i < len; i++){
11727                 var fieldError = errors[i];
11728                 var f = this.findField(fieldError.id);
11729                 if(f){
11730                     f.markInvalid(fieldError.msg);
11731                 }
11732             }
11733         }else{
11734             var field, id;
11735             for(id in errors){
11736                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11737                     field.markInvalid(errors[id]);
11738                 }
11739             }
11740         }
11741         //Roo.each(this.childForms || [], function (f) {
11742         //    f.markInvalid(errors);
11743         //});
11744
11745         return this;
11746     },
11747
11748     /**
11749      * Set values for fields in this form in bulk.
11750      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11751      * @return {BasicForm} this
11752      */
11753     setValues : function(values){
11754         if(values instanceof Array){ // array of objects
11755             for(var i = 0, len = values.length; i < len; i++){
11756                 var v = values[i];
11757                 var f = this.findField(v.id);
11758                 if(f){
11759                     f.setValue(v.value);
11760                     if(this.trackResetOnLoad){
11761                         f.originalValue = f.getValue();
11762                     }
11763                 }
11764             }
11765         }else{ // object hash
11766             var field, id;
11767             for(id in values){
11768                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11769
11770                     if (field.setFromData &&
11771                         field.valueField &&
11772                         field.displayField &&
11773                         // combos' with local stores can
11774                         // be queried via setValue()
11775                         // to set their value..
11776                         (field.store && !field.store.isLocal)
11777                         ) {
11778                         // it's a combo
11779                         var sd = { };
11780                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11781                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11782                         field.setFromData(sd);
11783
11784                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11785                         
11786                         field.setFromData(values);
11787                         
11788                     } else {
11789                         field.setValue(values[id]);
11790                     }
11791
11792
11793                     if(this.trackResetOnLoad){
11794                         field.originalValue = field.getValue();
11795                     }
11796                 }
11797             }
11798         }
11799
11800         //Roo.each(this.childForms || [], function (f) {
11801         //    f.setValues(values);
11802         //});
11803
11804         return this;
11805     },
11806
11807     /**
11808      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11809      * they are returned as an array.
11810      * @param {Boolean} asString
11811      * @return {Object}
11812      */
11813     getValues : function(asString){
11814         //if (this.childForms) {
11815             // copy values from the child forms
11816         //    Roo.each(this.childForms, function (f) {
11817         //        this.setValues(f.getValues());
11818         //    }, this);
11819         //}
11820
11821
11822
11823         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11824         if(asString === true){
11825             return fs;
11826         }
11827         return Roo.urlDecode(fs);
11828     },
11829
11830     /**
11831      * Returns the fields in this form as an object with key/value pairs.
11832      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11833      * @return {Object}
11834      */
11835     getFieldValues : function(with_hidden)
11836     {
11837         var items = this.getItems();
11838         var ret = {};
11839         items.each(function(f){
11840             
11841             if (!f.getName()) {
11842                 return;
11843             }
11844             
11845             var v = f.getValue();
11846             
11847             if (f.inputType =='radio') {
11848                 if (typeof(ret[f.getName()]) == 'undefined') {
11849                     ret[f.getName()] = ''; // empty..
11850                 }
11851
11852                 if (!f.el.dom.checked) {
11853                     return;
11854
11855                 }
11856                 v = f.el.dom.value;
11857
11858             }
11859             
11860             if(f.xtype == 'MoneyField'){
11861                 ret[f.currencyName] = f.getCurrency();
11862             }
11863
11864             // not sure if this supported any more..
11865             if ((typeof(v) == 'object') && f.getRawValue) {
11866                 v = f.getRawValue() ; // dates..
11867             }
11868             // combo boxes where name != hiddenName...
11869             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11870                 ret[f.name] = f.getRawValue();
11871             }
11872             ret[f.getName()] = v;
11873         });
11874
11875         return ret;
11876     },
11877
11878     /**
11879      * Clears all invalid messages in this form.
11880      * @return {BasicForm} this
11881      */
11882     clearInvalid : function(){
11883         var items = this.getItems();
11884
11885         items.each(function(f){
11886            f.clearInvalid();
11887         });
11888
11889         return this;
11890     },
11891
11892     /**
11893      * Resets this form.
11894      * @return {BasicForm} this
11895      */
11896     reset : function(){
11897         var items = this.getItems();
11898         items.each(function(f){
11899             f.reset();
11900         });
11901
11902         Roo.each(this.childForms || [], function (f) {
11903             f.reset();
11904         });
11905
11906
11907         return this;
11908     },
11909     
11910     getItems : function()
11911     {
11912         var r=new Roo.util.MixedCollection(false, function(o){
11913             return o.id || (o.id = Roo.id());
11914         });
11915         var iter = function(el) {
11916             if (el.inputEl) {
11917                 r.add(el);
11918             }
11919             if (!el.items) {
11920                 return;
11921             }
11922             Roo.each(el.items,function(e) {
11923                 iter(e);
11924             });
11925         };
11926
11927         iter(this);
11928         return r;
11929     },
11930     
11931     hideFields : function(items)
11932     {
11933         Roo.each(items, function(i){
11934             
11935             var f = this.findField(i);
11936             
11937             if(!f){
11938                 return;
11939             }
11940             
11941             f.hide();
11942             
11943         }, this);
11944     },
11945     
11946     showFields : function(items)
11947     {
11948         Roo.each(items, function(i){
11949             
11950             var f = this.findField(i);
11951             
11952             if(!f){
11953                 return;
11954             }
11955             
11956             f.show();
11957             
11958         }, this);
11959     }
11960
11961 });
11962
11963 Roo.apply(Roo.bootstrap.form.Form, {
11964     
11965     popover : {
11966         
11967         padding : 5,
11968         
11969         isApplied : false,
11970         
11971         isMasked : false,
11972         
11973         form : false,
11974         
11975         target : false,
11976         
11977         toolTip : false,
11978         
11979         intervalID : false,
11980         
11981         maskEl : false,
11982         
11983         apply : function()
11984         {
11985             if(this.isApplied){
11986                 return;
11987             }
11988             
11989             this.maskEl = {
11990                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11991                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11992                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11993                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11994             };
11995             
11996             this.maskEl.top.enableDisplayMode("block");
11997             this.maskEl.left.enableDisplayMode("block");
11998             this.maskEl.bottom.enableDisplayMode("block");
11999             this.maskEl.right.enableDisplayMode("block");
12000             
12001             this.toolTip = new Roo.bootstrap.Tooltip({
12002                 cls : 'roo-form-error-popover',
12003                 alignment : {
12004                     'left' : ['r-l', [-2,0], 'right'],
12005                     'right' : ['l-r', [2,0], 'left'],
12006                     'bottom' : ['tl-bl', [0,2], 'top'],
12007                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12008                 }
12009             });
12010             
12011             this.toolTip.render(Roo.get(document.body));
12012
12013             this.toolTip.el.enableDisplayMode("block");
12014             
12015             Roo.get(document.body).on('click', function(){
12016                 this.unmask();
12017             }, this);
12018             
12019             Roo.get(document.body).on('touchstart', function(){
12020                 this.unmask();
12021             }, this);
12022             
12023             this.isApplied = true
12024         },
12025         
12026         mask : function(form, target)
12027         {
12028             this.form = form;
12029             
12030             this.target = target;
12031             
12032             if(!this.form.errorMask || !target.el){
12033                 return;
12034             }
12035             
12036             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12037             
12038             Roo.log(scrollable);
12039             
12040             var ot = this.target.el.calcOffsetsTo(scrollable);
12041             
12042             var scrollTo = ot[1] - this.form.maskOffset;
12043             
12044             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12045             
12046             scrollable.scrollTo('top', scrollTo);
12047             
12048             var box = this.target.el.getBox();
12049             Roo.log(box);
12050             var zIndex = Roo.bootstrap.Modal.zIndex++;
12051
12052             
12053             this.maskEl.top.setStyle('position', 'absolute');
12054             this.maskEl.top.setStyle('z-index', zIndex);
12055             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12056             this.maskEl.top.setLeft(0);
12057             this.maskEl.top.setTop(0);
12058             this.maskEl.top.show();
12059             
12060             this.maskEl.left.setStyle('position', 'absolute');
12061             this.maskEl.left.setStyle('z-index', zIndex);
12062             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12063             this.maskEl.left.setLeft(0);
12064             this.maskEl.left.setTop(box.y - this.padding);
12065             this.maskEl.left.show();
12066
12067             this.maskEl.bottom.setStyle('position', 'absolute');
12068             this.maskEl.bottom.setStyle('z-index', zIndex);
12069             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12070             this.maskEl.bottom.setLeft(0);
12071             this.maskEl.bottom.setTop(box.bottom + this.padding);
12072             this.maskEl.bottom.show();
12073
12074             this.maskEl.right.setStyle('position', 'absolute');
12075             this.maskEl.right.setStyle('z-index', zIndex);
12076             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12077             this.maskEl.right.setLeft(box.right + this.padding);
12078             this.maskEl.right.setTop(box.y - this.padding);
12079             this.maskEl.right.show();
12080
12081             this.toolTip.bindEl = this.target.el;
12082
12083             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12084
12085             var tip = this.target.blankText;
12086
12087             if(this.target.getValue() !== '' ) {
12088                 
12089                 if (this.target.invalidText.length) {
12090                     tip = this.target.invalidText;
12091                 } else if (this.target.regexText.length){
12092                     tip = this.target.regexText;
12093                 }
12094             }
12095
12096             this.toolTip.show(tip);
12097
12098             this.intervalID = window.setInterval(function() {
12099                 Roo.bootstrap.form.Form.popover.unmask();
12100             }, 10000);
12101
12102             window.onwheel = function(){ return false;};
12103             
12104             (function(){ this.isMasked = true; }).defer(500, this);
12105             
12106         },
12107         
12108         unmask : function()
12109         {
12110             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12111                 return;
12112             }
12113             
12114             this.maskEl.top.setStyle('position', 'absolute');
12115             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12116             this.maskEl.top.hide();
12117
12118             this.maskEl.left.setStyle('position', 'absolute');
12119             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12120             this.maskEl.left.hide();
12121
12122             this.maskEl.bottom.setStyle('position', 'absolute');
12123             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12124             this.maskEl.bottom.hide();
12125
12126             this.maskEl.right.setStyle('position', 'absolute');
12127             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12128             this.maskEl.right.hide();
12129             
12130             this.toolTip.hide();
12131             
12132             this.toolTip.el.hide();
12133             
12134             window.onwheel = function(){ return true;};
12135             
12136             if(this.intervalID){
12137                 window.clearInterval(this.intervalID);
12138                 this.intervalID = false;
12139             }
12140             
12141             this.isMasked = false;
12142             
12143         }
12144         
12145     }
12146     
12147 });
12148
12149 /*
12150  * Based on:
12151  * Ext JS Library 1.1.1
12152  * Copyright(c) 2006-2007, Ext JS, LLC.
12153  *
12154  * Originally Released Under LGPL - original licence link has changed is not relivant.
12155  *
12156  * Fork - LGPL
12157  * <script type="text/javascript">
12158  */
12159 /**
12160  * @class Roo.form.VTypes
12161  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12162  * @static
12163  */
12164 Roo.form.VTypes = function(){
12165     // closure these in so they are only created once.
12166     var alpha = /^[a-zA-Z_]+$/;
12167     var alphanum = /^[a-zA-Z0-9_]+$/;
12168     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12169     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12170
12171     // All these messages and functions are configurable
12172     return {
12173         /**
12174          * The function used to validate email addresses
12175          * @param {String} value The email address
12176          */
12177         email : function(v){
12178             return email.test(v);
12179         },
12180         /**
12181          * The error text to display when the email validation function returns false
12182          * @type String
12183          */
12184         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12185         /**
12186          * The keystroke filter mask to be applied on email input
12187          * @type RegExp
12188          */
12189         emailMask : /[a-z0-9_\.\-@]/i,
12190
12191         /**
12192          * The function used to validate URLs
12193          * @param {String} value The URL
12194          */
12195         url : function(v){
12196             return url.test(v);
12197         },
12198         /**
12199          * The error text to display when the url validation function returns false
12200          * @type String
12201          */
12202         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12203         
12204         /**
12205          * The function used to validate alpha values
12206          * @param {String} value The value
12207          */
12208         alpha : function(v){
12209             return alpha.test(v);
12210         },
12211         /**
12212          * The error text to display when the alpha validation function returns false
12213          * @type String
12214          */
12215         alphaText : 'This field should only contain letters and _',
12216         /**
12217          * The keystroke filter mask to be applied on alpha input
12218          * @type RegExp
12219          */
12220         alphaMask : /[a-z_]/i,
12221
12222         /**
12223          * The function used to validate alphanumeric values
12224          * @param {String} value The value
12225          */
12226         alphanum : function(v){
12227             return alphanum.test(v);
12228         },
12229         /**
12230          * The error text to display when the alphanumeric validation function returns false
12231          * @type String
12232          */
12233         alphanumText : 'This field should only contain letters, numbers and _',
12234         /**
12235          * The keystroke filter mask to be applied on alphanumeric input
12236          * @type RegExp
12237          */
12238         alphanumMask : /[a-z0-9_]/i
12239     };
12240 }();/*
12241  * - LGPL
12242  *
12243  * Input
12244  * 
12245  */
12246
12247 /**
12248  * @class Roo.bootstrap.form.Input
12249  * @extends Roo.bootstrap.Component
12250  * Bootstrap Input class
12251  * @cfg {Boolean} disabled is it disabled
12252  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12253  * @cfg {String} name name of the input
12254  * @cfg {string} fieldLabel - the label associated
12255  * @cfg {string} placeholder - placeholder to put in text.
12256  * @cfg {string} before - input group add on before
12257  * @cfg {string} after - input group add on after
12258  * @cfg {string} size - (lg|sm) or leave empty..
12259  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12260  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12261  * @cfg {Number} md colspan out of 12 for computer-sized screens
12262  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12263  * @cfg {string} value default value of the input
12264  * @cfg {Number} labelWidth set the width of label 
12265  * @cfg {Number} labellg set the width of label (1-12)
12266  * @cfg {Number} labelmd set the width of label (1-12)
12267  * @cfg {Number} labelsm set the width of label (1-12)
12268  * @cfg {Number} labelxs set the width of label (1-12)
12269  * @cfg {String} labelAlign (top|left)
12270  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12271  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12272  * @cfg {String} indicatorpos (left|right) default left
12273  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12274  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12275  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12276  * @cfg {Roo.bootstrap.Button} before Button to show before
12277  * @cfg {Roo.bootstrap.Button} afterButton to show before
12278  * @cfg {String} align (left|center|right) Default left
12279  * @cfg {Boolean} forceFeedback (true|false) Default false
12280  * 
12281  * @constructor
12282  * Create a new Input
12283  * @param {Object} config The config object
12284  */
12285
12286 Roo.bootstrap.form.Input = function(config){
12287     
12288     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12289     
12290     this.addEvents({
12291         /**
12292          * @event focus
12293          * Fires when this field receives input focus.
12294          * @param {Roo.form.Field} this
12295          */
12296         focus : true,
12297         /**
12298          * @event blur
12299          * Fires when this field loses input focus.
12300          * @param {Roo.form.Field} this
12301          */
12302         blur : true,
12303         /**
12304          * @event specialkey
12305          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12306          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12307          * @param {Roo.form.Field} this
12308          * @param {Roo.EventObject} e The event object
12309          */
12310         specialkey : true,
12311         /**
12312          * @event change
12313          * Fires just before the field blurs if the field value has changed.
12314          * @param {Roo.form.Field} this
12315          * @param {Mixed} newValue The new value
12316          * @param {Mixed} oldValue The original value
12317          */
12318         change : true,
12319         /**
12320          * @event invalid
12321          * Fires after the field has been marked as invalid.
12322          * @param {Roo.form.Field} this
12323          * @param {String} msg The validation message
12324          */
12325         invalid : true,
12326         /**
12327          * @event valid
12328          * Fires after the field has been validated with no errors.
12329          * @param {Roo.form.Field} this
12330          */
12331         valid : true,
12332          /**
12333          * @event keyup
12334          * Fires after the key up
12335          * @param {Roo.form.Field} this
12336          * @param {Roo.EventObject}  e The event Object
12337          */
12338         keyup : true,
12339         /**
12340          * @event paste
12341          * Fires after the user pastes into input
12342          * @param {Roo.form.Field} this
12343          * @param {Roo.EventObject}  e The event Object
12344          */
12345         paste : true
12346     });
12347 };
12348
12349 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12350      /**
12351      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12352       automatic validation (defaults to "keyup").
12353      */
12354     validationEvent : "keyup",
12355      /**
12356      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12357      */
12358     validateOnBlur : true,
12359     /**
12360      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12361      */
12362     validationDelay : 250,
12363      /**
12364      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12365      */
12366     focusClass : "x-form-focus",  // not needed???
12367     
12368        
12369     /**
12370      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12371      */
12372     invalidClass : "has-warning",
12373     
12374     /**
12375      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12376      */
12377     validClass : "has-success",
12378     
12379     /**
12380      * @cfg {Boolean} hasFeedback (true|false) default true
12381      */
12382     hasFeedback : true,
12383     
12384     /**
12385      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12386      */
12387     invalidFeedbackClass : "glyphicon-warning-sign",
12388     
12389     /**
12390      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12391      */
12392     validFeedbackClass : "glyphicon-ok",
12393     
12394     /**
12395      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12396      */
12397     selectOnFocus : false,
12398     
12399      /**
12400      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12401      */
12402     maskRe : null,
12403        /**
12404      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12405      */
12406     vtype : null,
12407     
12408       /**
12409      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12410      */
12411     disableKeyFilter : false,
12412     
12413        /**
12414      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12415      */
12416     disabled : false,
12417      /**
12418      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12419      */
12420     allowBlank : true,
12421     /**
12422      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12423      */
12424     blankText : "Please complete this mandatory field",
12425     
12426      /**
12427      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12428      */
12429     minLength : 0,
12430     /**
12431      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12432      */
12433     maxLength : Number.MAX_VALUE,
12434     /**
12435      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12436      */
12437     minLengthText : "The minimum length for this field is {0}",
12438     /**
12439      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12440      */
12441     maxLengthText : "The maximum length for this field is {0}",
12442   
12443     
12444     /**
12445      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12446      * If available, this function will be called only after the basic validators all return true, and will be passed the
12447      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12448      */
12449     validator : null,
12450     /**
12451      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12452      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12453      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12454      */
12455     regex : null,
12456     /**
12457      * @cfg {String} regexText -- Depricated - use Invalid Text
12458      */
12459     regexText : "",
12460     
12461     /**
12462      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12463      */
12464     invalidText : "",
12465     
12466     
12467     
12468     autocomplete: false,
12469     
12470     
12471     fieldLabel : '',
12472     inputType : 'text',
12473     
12474     name : false,
12475     placeholder: false,
12476     before : false,
12477     after : false,
12478     size : false,
12479     hasFocus : false,
12480     preventMark: false,
12481     isFormField : true,
12482     value : '',
12483     labelWidth : 2,
12484     labelAlign : false,
12485     readOnly : false,
12486     align : false,
12487     formatedValue : false,
12488     forceFeedback : false,
12489     
12490     indicatorpos : 'left',
12491     
12492     labellg : 0,
12493     labelmd : 0,
12494     labelsm : 0,
12495     labelxs : 0,
12496     
12497     capture : '',
12498     accept : '',
12499     
12500     parentLabelAlign : function()
12501     {
12502         var parent = this;
12503         while (parent.parent()) {
12504             parent = parent.parent();
12505             if (typeof(parent.labelAlign) !='undefined') {
12506                 return parent.labelAlign;
12507             }
12508         }
12509         return 'left';
12510         
12511     },
12512     
12513     getAutoCreate : function()
12514     {
12515         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12516         
12517         var id = Roo.id();
12518         
12519         var cfg = {};
12520         
12521         if(this.inputType != 'hidden'){
12522             cfg.cls = 'form-group' //input-group
12523         }
12524         
12525         var input =  {
12526             tag: 'input',
12527             id : id,
12528             type : this.inputType,
12529             value : this.value,
12530             cls : 'form-control',
12531             placeholder : this.placeholder || '',
12532             autocomplete : this.autocomplete || 'new-password'
12533         };
12534         if (this.inputType == 'file') {
12535             input.style = 'overflow:hidden'; // why not in CSS?
12536         }
12537         
12538         if(this.capture.length){
12539             input.capture = this.capture;
12540         }
12541         
12542         if(this.accept.length){
12543             input.accept = this.accept + "/*";
12544         }
12545         
12546         if(this.align){
12547             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12548         }
12549         
12550         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12551             input.maxLength = this.maxLength;
12552         }
12553         
12554         if (this.disabled) {
12555             input.disabled=true;
12556         }
12557         
12558         if (this.readOnly) {
12559             input.readonly=true;
12560         }
12561         
12562         if (this.name) {
12563             input.name = this.name;
12564         }
12565         
12566         if (this.size) {
12567             input.cls += ' input-' + this.size;
12568         }
12569         
12570         var settings=this;
12571         ['xs','sm','md','lg'].map(function(size){
12572             if (settings[size]) {
12573                 cfg.cls += ' col-' + size + '-' + settings[size];
12574             }
12575         });
12576         
12577         var inputblock = input;
12578         
12579         var feedback = {
12580             tag: 'span',
12581             cls: 'glyphicon form-control-feedback'
12582         };
12583             
12584         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12585             
12586             inputblock = {
12587                 cls : 'has-feedback',
12588                 cn :  [
12589                     input,
12590                     feedback
12591                 ] 
12592             };  
12593         }
12594         
12595         if (this.before || this.after) {
12596             
12597             inputblock = {
12598                 cls : 'input-group',
12599                 cn :  [] 
12600             };
12601             
12602             if (this.before && typeof(this.before) == 'string') {
12603                 
12604                 inputblock.cn.push({
12605                     tag :'span',
12606                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12607                     html : this.before
12608                 });
12609             }
12610             if (this.before && typeof(this.before) == 'object') {
12611                 this.before = Roo.factory(this.before);
12612                 
12613                 inputblock.cn.push({
12614                     tag :'span',
12615                     cls : 'roo-input-before input-group-prepend   input-group-' +
12616                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12617                 });
12618             }
12619             
12620             inputblock.cn.push(input);
12621             
12622             if (this.after && typeof(this.after) == 'string') {
12623                 inputblock.cn.push({
12624                     tag :'span',
12625                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12626                     html : this.after
12627                 });
12628             }
12629             if (this.after && typeof(this.after) == 'object') {
12630                 this.after = Roo.factory(this.after);
12631                 
12632                 inputblock.cn.push({
12633                     tag :'span',
12634                     cls : 'roo-input-after input-group-append  input-group-' +
12635                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12636                 });
12637             }
12638             
12639             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12640                 inputblock.cls += ' has-feedback';
12641                 inputblock.cn.push(feedback);
12642             }
12643         };
12644         var indicator = {
12645             tag : 'i',
12646             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12647             tooltip : 'This field is required'
12648         };
12649         if (this.allowBlank ) {
12650             indicator.style = this.allowBlank ? ' display:none' : '';
12651         }
12652         if (align ==='left' && this.fieldLabel.length) {
12653             
12654             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12655             
12656             cfg.cn = [
12657                 indicator,
12658                 {
12659                     tag: 'label',
12660                     'for' :  id,
12661                     cls : 'control-label col-form-label',
12662                     html : this.fieldLabel
12663
12664                 },
12665                 {
12666                     cls : "", 
12667                     cn: [
12668                         inputblock
12669                     ]
12670                 }
12671             ];
12672             
12673             var labelCfg = cfg.cn[1];
12674             var contentCfg = cfg.cn[2];
12675             
12676             if(this.indicatorpos == 'right'){
12677                 cfg.cn = [
12678                     {
12679                         tag: 'label',
12680                         'for' :  id,
12681                         cls : 'control-label col-form-label',
12682                         cn : [
12683                             {
12684                                 tag : 'span',
12685                                 html : this.fieldLabel
12686                             },
12687                             indicator
12688                         ]
12689                     },
12690                     {
12691                         cls : "",
12692                         cn: [
12693                             inputblock
12694                         ]
12695                     }
12696
12697                 ];
12698                 
12699                 labelCfg = cfg.cn[0];
12700                 contentCfg = cfg.cn[1];
12701             
12702             }
12703             
12704             if(this.labelWidth > 12){
12705                 labelCfg.style = "width: " + this.labelWidth + 'px';
12706             }
12707             
12708             if(this.labelWidth < 13 && this.labelmd == 0){
12709                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12710             }
12711             
12712             if(this.labellg > 0){
12713                 labelCfg.cls += ' col-lg-' + this.labellg;
12714                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12715             }
12716             
12717             if(this.labelmd > 0){
12718                 labelCfg.cls += ' col-md-' + this.labelmd;
12719                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12720             }
12721             
12722             if(this.labelsm > 0){
12723                 labelCfg.cls += ' col-sm-' + this.labelsm;
12724                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12725             }
12726             
12727             if(this.labelxs > 0){
12728                 labelCfg.cls += ' col-xs-' + this.labelxs;
12729                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12730             }
12731             
12732             
12733         } else if ( this.fieldLabel.length) {
12734                 
12735             
12736             
12737             cfg.cn = [
12738                 {
12739                     tag : 'i',
12740                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12741                     tooltip : 'This field is required',
12742                     style : this.allowBlank ? ' display:none' : '' 
12743                 },
12744                 {
12745                     tag: 'label',
12746                    //cls : 'input-group-addon',
12747                     html : this.fieldLabel
12748
12749                 },
12750
12751                inputblock
12752
12753            ];
12754            
12755            if(this.indicatorpos == 'right'){
12756        
12757                 cfg.cn = [
12758                     {
12759                         tag: 'label',
12760                        //cls : 'input-group-addon',
12761                         html : this.fieldLabel
12762
12763                     },
12764                     {
12765                         tag : 'i',
12766                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12767                         tooltip : 'This field is required',
12768                         style : this.allowBlank ? ' display:none' : '' 
12769                     },
12770
12771                    inputblock
12772
12773                ];
12774
12775             }
12776
12777         } else {
12778             
12779             cfg.cn = [
12780
12781                     inputblock
12782
12783             ];
12784                 
12785                 
12786         };
12787         
12788         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12789            cfg.cls += ' navbar-form';
12790         }
12791         
12792         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12793             // on BS4 we do this only if not form 
12794             cfg.cls += ' navbar-form';
12795             cfg.tag = 'li';
12796         }
12797         
12798         return cfg;
12799         
12800     },
12801     /**
12802      * return the real input element.
12803      */
12804     inputEl: function ()
12805     {
12806         return this.el.select('input.form-control',true).first();
12807     },
12808     
12809     tooltipEl : function()
12810     {
12811         return this.inputEl();
12812     },
12813     
12814     indicatorEl : function()
12815     {
12816         if (Roo.bootstrap.version == 4) {
12817             return false; // not enabled in v4 yet.
12818         }
12819         
12820         var indicator = this.el.select('i.roo-required-indicator',true).first();
12821         
12822         if(!indicator){
12823             return false;
12824         }
12825         
12826         return indicator;
12827         
12828     },
12829     
12830     setDisabled : function(v)
12831     {
12832         var i  = this.inputEl().dom;
12833         if (!v) {
12834             i.removeAttribute('disabled');
12835             return;
12836             
12837         }
12838         i.setAttribute('disabled','true');
12839     },
12840     initEvents : function()
12841     {
12842           
12843         this.inputEl().on("keydown" , this.fireKey,  this);
12844         this.inputEl().on("focus", this.onFocus,  this);
12845         this.inputEl().on("blur", this.onBlur,  this);
12846         
12847         this.inputEl().relayEvent('keyup', this);
12848         this.inputEl().relayEvent('paste', this);
12849         
12850         this.indicator = this.indicatorEl();
12851         
12852         if(this.indicator){
12853             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12854         }
12855  
12856         // reference to original value for reset
12857         this.originalValue = this.getValue();
12858         //Roo.form.TextField.superclass.initEvents.call(this);
12859         if(this.validationEvent == 'keyup'){
12860             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12861             this.inputEl().on('keyup', this.filterValidation, this);
12862         }
12863         else if(this.validationEvent !== false){
12864             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12865         }
12866         
12867         if(this.selectOnFocus){
12868             this.on("focus", this.preFocus, this);
12869             
12870         }
12871         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12872             this.inputEl().on("keypress", this.filterKeys, this);
12873         } else {
12874             this.inputEl().relayEvent('keypress', this);
12875         }
12876        /* if(this.grow){
12877             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12878             this.el.on("click", this.autoSize,  this);
12879         }
12880         */
12881         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12882             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12883         }
12884         
12885         if (typeof(this.before) == 'object') {
12886             this.before.render(this.el.select('.roo-input-before',true).first());
12887         }
12888         if (typeof(this.after) == 'object') {
12889             this.after.render(this.el.select('.roo-input-after',true).first());
12890         }
12891         
12892         this.inputEl().on('change', this.onChange, this);
12893         
12894     },
12895     filterValidation : function(e){
12896         if(!e.isNavKeyPress()){
12897             this.validationTask.delay(this.validationDelay);
12898         }
12899     },
12900      /**
12901      * Validates the field value
12902      * @return {Boolean} True if the value is valid, else false
12903      */
12904     validate : function(){
12905         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12906         if(this.disabled || this.validateValue(this.getRawValue())){
12907             this.markValid();
12908             return true;
12909         }
12910         
12911         this.markInvalid();
12912         return false;
12913     },
12914     
12915     
12916     /**
12917      * Validates a value according to the field's validation rules and marks the field as invalid
12918      * if the validation fails
12919      * @param {Mixed} value The value to validate
12920      * @return {Boolean} True if the value is valid, else false
12921      */
12922     validateValue : function(value)
12923     {
12924         if(this.getVisibilityEl().hasClass('hidden')){
12925             return true;
12926         }
12927         
12928         if(value.length < 1)  { // if it's blank
12929             if(this.allowBlank){
12930                 return true;
12931             }
12932             return false;
12933         }
12934         
12935         if(value.length < this.minLength){
12936             return false;
12937         }
12938         if(value.length > this.maxLength){
12939             return false;
12940         }
12941         if(this.vtype){
12942             var vt = Roo.form.VTypes;
12943             if(!vt[this.vtype](value, this)){
12944                 return false;
12945             }
12946         }
12947         if(typeof this.validator == "function"){
12948             var msg = this.validator(value);
12949             if (typeof(msg) == 'string') {
12950                 this.invalidText = msg;
12951             }
12952             if(msg !== true){
12953                 return false;
12954             }
12955         }
12956         
12957         if(this.regex && !this.regex.test(value)){
12958             return false;
12959         }
12960         
12961         return true;
12962     },
12963     
12964      // private
12965     fireKey : function(e){
12966         //Roo.log('field ' + e.getKey());
12967         if(e.isNavKeyPress()){
12968             this.fireEvent("specialkey", this, e);
12969         }
12970     },
12971     focus : function (selectText){
12972         if(this.rendered){
12973             this.inputEl().focus();
12974             if(selectText === true){
12975                 this.inputEl().dom.select();
12976             }
12977         }
12978         return this;
12979     } ,
12980     
12981     onFocus : function(){
12982         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12983            // this.el.addClass(this.focusClass);
12984         }
12985         if(!this.hasFocus){
12986             this.hasFocus = true;
12987             this.startValue = this.getValue();
12988             this.fireEvent("focus", this);
12989         }
12990     },
12991     
12992     beforeBlur : Roo.emptyFn,
12993
12994     
12995     // private
12996     onBlur : function(){
12997         this.beforeBlur();
12998         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12999             //this.el.removeClass(this.focusClass);
13000         }
13001         this.hasFocus = false;
13002         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13003             this.validate();
13004         }
13005         var v = this.getValue();
13006         if(String(v) !== String(this.startValue)){
13007             this.fireEvent('change', this, v, this.startValue);
13008         }
13009         this.fireEvent("blur", this);
13010     },
13011     
13012     onChange : function(e)
13013     {
13014         var v = this.getValue();
13015         if(String(v) !== String(this.startValue)){
13016             this.fireEvent('change', this, v, this.startValue);
13017         }
13018         
13019     },
13020     
13021     /**
13022      * Resets the current field value to the originally loaded value and clears any validation messages
13023      */
13024     reset : function(){
13025         this.setValue(this.originalValue);
13026         this.validate();
13027     },
13028      /**
13029      * Returns the name of the field
13030      * @return {Mixed} name The name field
13031      */
13032     getName: function(){
13033         return this.name;
13034     },
13035      /**
13036      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13037      * @return {Mixed} value The field value
13038      */
13039     getValue : function(){
13040         
13041         var v = this.inputEl().getValue();
13042         
13043         return v;
13044     },
13045     /**
13046      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13047      * @return {Mixed} value The field value
13048      */
13049     getRawValue : function(){
13050         var v = this.inputEl().getValue();
13051         
13052         return v;
13053     },
13054     
13055     /**
13056      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13057      * @param {Mixed} value The value to set
13058      */
13059     setRawValue : function(v){
13060         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13061     },
13062     
13063     selectText : function(start, end){
13064         var v = this.getRawValue();
13065         if(v.length > 0){
13066             start = start === undefined ? 0 : start;
13067             end = end === undefined ? v.length : end;
13068             var d = this.inputEl().dom;
13069             if(d.setSelectionRange){
13070                 d.setSelectionRange(start, end);
13071             }else if(d.createTextRange){
13072                 var range = d.createTextRange();
13073                 range.moveStart("character", start);
13074                 range.moveEnd("character", v.length-end);
13075                 range.select();
13076             }
13077         }
13078     },
13079     
13080     /**
13081      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13082      * @param {Mixed} value The value to set
13083      */
13084     setValue : function(v){
13085         this.value = v;
13086         if(this.rendered){
13087             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13088             this.validate();
13089         }
13090     },
13091     
13092     /*
13093     processValue : function(value){
13094         if(this.stripCharsRe){
13095             var newValue = value.replace(this.stripCharsRe, '');
13096             if(newValue !== value){
13097                 this.setRawValue(newValue);
13098                 return newValue;
13099             }
13100         }
13101         return value;
13102     },
13103   */
13104     preFocus : function(){
13105         
13106         if(this.selectOnFocus){
13107             this.inputEl().dom.select();
13108         }
13109     },
13110     filterKeys : function(e){
13111         var k = e.getKey();
13112         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13113             return;
13114         }
13115         var c = e.getCharCode(), cc = String.fromCharCode(c);
13116         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13117             return;
13118         }
13119         if(!this.maskRe.test(cc)){
13120             e.stopEvent();
13121         }
13122     },
13123      /**
13124      * Clear any invalid styles/messages for this field
13125      */
13126     clearInvalid : function(){
13127         
13128         if(!this.el || this.preventMark){ // not rendered
13129             return;
13130         }
13131         
13132         
13133         this.el.removeClass([this.invalidClass, 'is-invalid']);
13134         
13135         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13136             
13137             var feedback = this.el.select('.form-control-feedback', true).first();
13138             
13139             if(feedback){
13140                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13141             }
13142             
13143         }
13144         
13145         if(this.indicator){
13146             this.indicator.removeClass('visible');
13147             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13148         }
13149         
13150         this.fireEvent('valid', this);
13151     },
13152     
13153      /**
13154      * Mark this field as valid
13155      */
13156     markValid : function()
13157     {
13158         if(!this.el  || this.preventMark){ // not rendered...
13159             return;
13160         }
13161         
13162         this.el.removeClass([this.invalidClass, this.validClass]);
13163         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13164
13165         var feedback = this.el.select('.form-control-feedback', true).first();
13166             
13167         if(feedback){
13168             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13169         }
13170         
13171         if(this.indicator){
13172             this.indicator.removeClass('visible');
13173             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13174         }
13175         
13176         if(this.disabled){
13177             return;
13178         }
13179         
13180            
13181         if(this.allowBlank && !this.getRawValue().length){
13182             return;
13183         }
13184         if (Roo.bootstrap.version == 3) {
13185             this.el.addClass(this.validClass);
13186         } else {
13187             this.inputEl().addClass('is-valid');
13188         }
13189
13190         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13191             
13192             var feedback = this.el.select('.form-control-feedback', true).first();
13193             
13194             if(feedback){
13195                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13196                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13197             }
13198             
13199         }
13200         
13201         this.fireEvent('valid', this);
13202     },
13203     
13204      /**
13205      * Mark this field as invalid
13206      * @param {String} msg The validation message
13207      */
13208     markInvalid : function(msg)
13209     {
13210         if(!this.el  || this.preventMark){ // not rendered
13211             return;
13212         }
13213         
13214         this.el.removeClass([this.invalidClass, this.validClass]);
13215         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13216         
13217         var feedback = this.el.select('.form-control-feedback', true).first();
13218             
13219         if(feedback){
13220             this.el.select('.form-control-feedback', true).first().removeClass(
13221                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13222         }
13223
13224         if(this.disabled){
13225             return;
13226         }
13227         
13228         if(this.allowBlank && !this.getRawValue().length){
13229             return;
13230         }
13231         
13232         if(this.indicator){
13233             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13234             this.indicator.addClass('visible');
13235         }
13236         if (Roo.bootstrap.version == 3) {
13237             this.el.addClass(this.invalidClass);
13238         } else {
13239             this.inputEl().addClass('is-invalid');
13240         }
13241         
13242         
13243         
13244         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13245             
13246             var feedback = this.el.select('.form-control-feedback', true).first();
13247             
13248             if(feedback){
13249                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13250                 
13251                 if(this.getValue().length || this.forceFeedback){
13252                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13253                 }
13254                 
13255             }
13256             
13257         }
13258         
13259         this.fireEvent('invalid', this, msg);
13260     },
13261     // private
13262     SafariOnKeyDown : function(event)
13263     {
13264         // this is a workaround for a password hang bug on chrome/ webkit.
13265         if (this.inputEl().dom.type != 'password') {
13266             return;
13267         }
13268         
13269         var isSelectAll = false;
13270         
13271         if(this.inputEl().dom.selectionEnd > 0){
13272             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13273         }
13274         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13275             event.preventDefault();
13276             this.setValue('');
13277             return;
13278         }
13279         
13280         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13281             
13282             event.preventDefault();
13283             // this is very hacky as keydown always get's upper case.
13284             //
13285             var cc = String.fromCharCode(event.getCharCode());
13286             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13287             
13288         }
13289     },
13290     adjustWidth : function(tag, w){
13291         tag = tag.toLowerCase();
13292         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13293             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13294                 if(tag == 'input'){
13295                     return w + 2;
13296                 }
13297                 if(tag == 'textarea'){
13298                     return w-2;
13299                 }
13300             }else if(Roo.isOpera){
13301                 if(tag == 'input'){
13302                     return w + 2;
13303                 }
13304                 if(tag == 'textarea'){
13305                     return w-2;
13306                 }
13307             }
13308         }
13309         return w;
13310     },
13311     
13312     setFieldLabel : function(v)
13313     {
13314         if(!this.rendered){
13315             return;
13316         }
13317         
13318         if(this.indicatorEl()){
13319             var ar = this.el.select('label > span',true);
13320             
13321             if (ar.elements.length) {
13322                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13323                 this.fieldLabel = v;
13324                 return;
13325             }
13326             
13327             var br = this.el.select('label',true);
13328             
13329             if(br.elements.length) {
13330                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13331                 this.fieldLabel = v;
13332                 return;
13333             }
13334             
13335             Roo.log('Cannot Found any of label > span || label in input');
13336             return;
13337         }
13338         
13339         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13340         this.fieldLabel = v;
13341         
13342         
13343     }
13344 });
13345
13346  
13347 /*
13348  * - LGPL
13349  *
13350  * Input
13351  * 
13352  */
13353
13354 /**
13355  * @class Roo.bootstrap.form.TextArea
13356  * @extends Roo.bootstrap.form.Input
13357  * Bootstrap TextArea class
13358  * @cfg {Number} cols Specifies the visible width of a text area
13359  * @cfg {Number} rows Specifies the visible number of lines in a text area
13360  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13361  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13362  * @cfg {string} html text
13363  * 
13364  * @constructor
13365  * Create a new TextArea
13366  * @param {Object} config The config object
13367  */
13368
13369 Roo.bootstrap.form.TextArea = function(config){
13370     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13371    
13372 };
13373
13374 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13375      
13376     cols : false,
13377     rows : 5,
13378     readOnly : false,
13379     warp : 'soft',
13380     resize : false,
13381     value: false,
13382     html: false,
13383     
13384     getAutoCreate : function(){
13385         
13386         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13387         
13388         var id = Roo.id();
13389         
13390         var cfg = {};
13391         
13392         if(this.inputType != 'hidden'){
13393             cfg.cls = 'form-group' //input-group
13394         }
13395         
13396         var input =  {
13397             tag: 'textarea',
13398             id : id,
13399             warp : this.warp,
13400             rows : this.rows,
13401             value : this.value || '',
13402             html: this.html || '',
13403             cls : 'form-control',
13404             placeholder : this.placeholder || '' 
13405             
13406         };
13407         
13408         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13409             input.maxLength = this.maxLength;
13410         }
13411         
13412         if(this.resize){
13413             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13414         }
13415         
13416         if(this.cols){
13417             input.cols = this.cols;
13418         }
13419         
13420         if (this.readOnly) {
13421             input.readonly = true;
13422         }
13423         
13424         if (this.name) {
13425             input.name = this.name;
13426         }
13427         
13428         if (this.size) {
13429             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13430         }
13431         
13432         var settings=this;
13433         ['xs','sm','md','lg'].map(function(size){
13434             if (settings[size]) {
13435                 cfg.cls += ' col-' + size + '-' + settings[size];
13436             }
13437         });
13438         
13439         var inputblock = input;
13440         
13441         if(this.hasFeedback && !this.allowBlank){
13442             
13443             var feedback = {
13444                 tag: 'span',
13445                 cls: 'glyphicon form-control-feedback'
13446             };
13447
13448             inputblock = {
13449                 cls : 'has-feedback',
13450                 cn :  [
13451                     input,
13452                     feedback
13453                 ] 
13454             };  
13455         }
13456         
13457         
13458         if (this.before || this.after) {
13459             
13460             inputblock = {
13461                 cls : 'input-group',
13462                 cn :  [] 
13463             };
13464             if (this.before) {
13465                 inputblock.cn.push({
13466                     tag :'span',
13467                     cls : 'input-group-addon',
13468                     html : this.before
13469                 });
13470             }
13471             
13472             inputblock.cn.push(input);
13473             
13474             if(this.hasFeedback && !this.allowBlank){
13475                 inputblock.cls += ' has-feedback';
13476                 inputblock.cn.push(feedback);
13477             }
13478             
13479             if (this.after) {
13480                 inputblock.cn.push({
13481                     tag :'span',
13482                     cls : 'input-group-addon',
13483                     html : this.after
13484                 });
13485             }
13486             
13487         }
13488         
13489         if (align ==='left' && this.fieldLabel.length) {
13490             cfg.cn = [
13491                 {
13492                     tag: 'label',
13493                     'for' :  id,
13494                     cls : 'control-label',
13495                     html : this.fieldLabel
13496                 },
13497                 {
13498                     cls : "",
13499                     cn: [
13500                         inputblock
13501                     ]
13502                 }
13503
13504             ];
13505             
13506             if(this.labelWidth > 12){
13507                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13508             }
13509
13510             if(this.labelWidth < 13 && this.labelmd == 0){
13511                 this.labelmd = this.labelWidth;
13512             }
13513
13514             if(this.labellg > 0){
13515                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13516                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13517             }
13518
13519             if(this.labelmd > 0){
13520                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13521                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13522             }
13523
13524             if(this.labelsm > 0){
13525                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13526                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13527             }
13528
13529             if(this.labelxs > 0){
13530                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13531                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13532             }
13533             
13534         } else if ( this.fieldLabel.length) {
13535             cfg.cn = [
13536
13537                {
13538                    tag: 'label',
13539                    //cls : 'input-group-addon',
13540                    html : this.fieldLabel
13541
13542                },
13543
13544                inputblock
13545
13546            ];
13547
13548         } else {
13549
13550             cfg.cn = [
13551
13552                 inputblock
13553
13554             ];
13555                 
13556         }
13557         
13558         if (this.disabled) {
13559             input.disabled=true;
13560         }
13561         
13562         return cfg;
13563         
13564     },
13565     /**
13566      * return the real textarea element.
13567      */
13568     inputEl: function ()
13569     {
13570         return this.el.select('textarea.form-control',true).first();
13571     },
13572     
13573     /**
13574      * Clear any invalid styles/messages for this field
13575      */
13576     clearInvalid : function()
13577     {
13578         
13579         if(!this.el || this.preventMark){ // not rendered
13580             return;
13581         }
13582         
13583         var label = this.el.select('label', true).first();
13584         var icon = this.el.select('i.fa-star', true).first();
13585         
13586         if(label && icon){
13587             icon.remove();
13588         }
13589         this.el.removeClass( this.validClass);
13590         this.inputEl().removeClass('is-invalid');
13591          
13592         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13593             
13594             var feedback = this.el.select('.form-control-feedback', true).first();
13595             
13596             if(feedback){
13597                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13598             }
13599             
13600         }
13601         
13602         this.fireEvent('valid', this);
13603     },
13604     
13605      /**
13606      * Mark this field as valid
13607      */
13608     markValid : function()
13609     {
13610         if(!this.el  || this.preventMark){ // not rendered
13611             return;
13612         }
13613         
13614         this.el.removeClass([this.invalidClass, this.validClass]);
13615         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13616         
13617         var feedback = this.el.select('.form-control-feedback', true).first();
13618             
13619         if(feedback){
13620             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13621         }
13622
13623         if(this.disabled || this.allowBlank){
13624             return;
13625         }
13626         
13627         var label = this.el.select('label', true).first();
13628         var icon = this.el.select('i.fa-star', true).first();
13629         
13630         if(label && icon){
13631             icon.remove();
13632         }
13633         if (Roo.bootstrap.version == 3) {
13634             this.el.addClass(this.validClass);
13635         } else {
13636             this.inputEl().addClass('is-valid');
13637         }
13638         
13639         
13640         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13641             
13642             var feedback = this.el.select('.form-control-feedback', true).first();
13643             
13644             if(feedback){
13645                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13646                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13647             }
13648             
13649         }
13650         
13651         this.fireEvent('valid', this);
13652     },
13653     
13654      /**
13655      * Mark this field as invalid
13656      * @param {String} msg The validation message
13657      */
13658     markInvalid : function(msg)
13659     {
13660         if(!this.el  || this.preventMark){ // not rendered
13661             return;
13662         }
13663         
13664         this.el.removeClass([this.invalidClass, this.validClass]);
13665         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13666         
13667         var feedback = this.el.select('.form-control-feedback', true).first();
13668             
13669         if(feedback){
13670             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13671         }
13672
13673         if(this.disabled || this.allowBlank){
13674             return;
13675         }
13676         
13677         var label = this.el.select('label', true).first();
13678         var icon = this.el.select('i.fa-star', true).first();
13679         
13680         if(!this.getValue().length && label && !icon){
13681             this.el.createChild({
13682                 tag : 'i',
13683                 cls : 'text-danger fa fa-lg fa-star',
13684                 tooltip : 'This field is required',
13685                 style : 'margin-right:5px;'
13686             }, label, true);
13687         }
13688         
13689         if (Roo.bootstrap.version == 3) {
13690             this.el.addClass(this.invalidClass);
13691         } else {
13692             this.inputEl().addClass('is-invalid');
13693         }
13694         
13695         // fixme ... this may be depricated need to test..
13696         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13697             
13698             var feedback = this.el.select('.form-control-feedback', true).first();
13699             
13700             if(feedback){
13701                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13702                 
13703                 if(this.getValue().length || this.forceFeedback){
13704                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13705                 }
13706                 
13707             }
13708             
13709         }
13710         
13711         this.fireEvent('invalid', this, msg);
13712     }
13713 });
13714
13715  
13716 /*
13717  * - LGPL
13718  *
13719  * trigger field - base class for combo..
13720  * 
13721  */
13722  
13723 /**
13724  * @class Roo.bootstrap.form.TriggerField
13725  * @extends Roo.bootstrap.form.Input
13726  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13727  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13728  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13729  * for which you can provide a custom implementation.  For example:
13730  * <pre><code>
13731 var trigger = new Roo.bootstrap.form.TriggerField();
13732 trigger.onTriggerClick = myTriggerFn;
13733 trigger.applyTo('my-field');
13734 </code></pre>
13735  *
13736  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13737  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13738  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13739  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13740  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13741
13742  * @constructor
13743  * Create a new TriggerField.
13744  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13745  * to the base TextField)
13746  */
13747 Roo.bootstrap.form.TriggerField = function(config){
13748     this.mimicing = false;
13749     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13750 };
13751
13752 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13753     /**
13754      * @cfg {String} triggerClass A CSS class to apply to the trigger
13755      */
13756      /**
13757      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13758      */
13759     hideTrigger:false,
13760
13761     /**
13762      * @cfg {Boolean} removable (true|false) special filter default false
13763      */
13764     removable : false,
13765     
13766     /** @cfg {Boolean} grow @hide */
13767     /** @cfg {Number} growMin @hide */
13768     /** @cfg {Number} growMax @hide */
13769
13770     /**
13771      * @hide 
13772      * @method
13773      */
13774     autoSize: Roo.emptyFn,
13775     // private
13776     monitorTab : true,
13777     // private
13778     deferHeight : true,
13779
13780     
13781     actionMode : 'wrap',
13782     
13783     caret : false,
13784     
13785     
13786     getAutoCreate : function(){
13787        
13788         var align = this.labelAlign || this.parentLabelAlign();
13789         
13790         var id = Roo.id();
13791         
13792         var cfg = {
13793             cls: 'form-group' //input-group
13794         };
13795         
13796         
13797         var input =  {
13798             tag: 'input',
13799             id : id,
13800             type : this.inputType,
13801             cls : 'form-control',
13802             autocomplete: 'new-password',
13803             placeholder : this.placeholder || '' 
13804             
13805         };
13806         if (this.name) {
13807             input.name = this.name;
13808         }
13809         if (this.size) {
13810             input.cls += ' input-' + this.size;
13811         }
13812         
13813         if (this.disabled) {
13814             input.disabled=true;
13815         }
13816         
13817         var inputblock = input;
13818         
13819         if(this.hasFeedback && !this.allowBlank){
13820             
13821             var feedback = {
13822                 tag: 'span',
13823                 cls: 'glyphicon form-control-feedback'
13824             };
13825             
13826             if(this.removable && !this.editable  ){
13827                 inputblock = {
13828                     cls : 'has-feedback',
13829                     cn :  [
13830                         inputblock,
13831                         {
13832                             tag: 'button',
13833                             html : 'x',
13834                             cls : 'roo-combo-removable-btn close'
13835                         },
13836                         feedback
13837                     ] 
13838                 };
13839             } else {
13840                 inputblock = {
13841                     cls : 'has-feedback',
13842                     cn :  [
13843                         inputblock,
13844                         feedback
13845                     ] 
13846                 };
13847             }
13848
13849         } else {
13850             if(this.removable && !this.editable ){
13851                 inputblock = {
13852                     cls : 'roo-removable',
13853                     cn :  [
13854                         inputblock,
13855                         {
13856                             tag: 'button',
13857                             html : 'x',
13858                             cls : 'roo-combo-removable-btn close'
13859                         }
13860                     ] 
13861                 };
13862             }
13863         }
13864         
13865         if (this.before || this.after) {
13866             
13867             inputblock = {
13868                 cls : 'input-group',
13869                 cn :  [] 
13870             };
13871             if (this.before) {
13872                 inputblock.cn.push({
13873                     tag :'span',
13874                     cls : 'input-group-addon input-group-prepend input-group-text',
13875                     html : this.before
13876                 });
13877             }
13878             
13879             inputblock.cn.push(input);
13880             
13881             if(this.hasFeedback && !this.allowBlank){
13882                 inputblock.cls += ' has-feedback';
13883                 inputblock.cn.push(feedback);
13884             }
13885             
13886             if (this.after) {
13887                 inputblock.cn.push({
13888                     tag :'span',
13889                     cls : 'input-group-addon input-group-append input-group-text',
13890                     html : this.after
13891                 });
13892             }
13893             
13894         };
13895         
13896       
13897         
13898         var ibwrap = inputblock;
13899         
13900         if(this.multiple){
13901             ibwrap = {
13902                 tag: 'ul',
13903                 cls: 'roo-select2-choices',
13904                 cn:[
13905                     {
13906                         tag: 'li',
13907                         cls: 'roo-select2-search-field',
13908                         cn: [
13909
13910                             inputblock
13911                         ]
13912                     }
13913                 ]
13914             };
13915                 
13916         }
13917         
13918         var combobox = {
13919             cls: 'roo-select2-container input-group',
13920             cn: [
13921                  {
13922                     tag: 'input',
13923                     type : 'hidden',
13924                     cls: 'form-hidden-field'
13925                 },
13926                 ibwrap
13927             ]
13928         };
13929         
13930         if(!this.multiple && this.showToggleBtn){
13931             
13932             var caret = {
13933                         tag: 'span',
13934                         cls: 'caret'
13935              };
13936             if (this.caret != false) {
13937                 caret = {
13938                      tag: 'i',
13939                      cls: 'fa fa-' + this.caret
13940                 };
13941                 
13942             }
13943             
13944             combobox.cn.push({
13945                 tag :'span',
13946                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13947                 cn : [
13948                     Roo.bootstrap.version == 3 ? caret : '',
13949                     {
13950                         tag: 'span',
13951                         cls: 'combobox-clear',
13952                         cn  : [
13953                             {
13954                                 tag : 'i',
13955                                 cls: 'icon-remove'
13956                             }
13957                         ]
13958                     }
13959                 ]
13960
13961             })
13962         }
13963         
13964         if(this.multiple){
13965             combobox.cls += ' roo-select2-container-multi';
13966         }
13967          var indicator = {
13968             tag : 'i',
13969             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13970             tooltip : 'This field is required'
13971         };
13972         if (Roo.bootstrap.version == 4) {
13973             indicator = {
13974                 tag : 'i',
13975                 style : 'display:none'
13976             };
13977         }
13978         
13979         
13980         if (align ==='left' && this.fieldLabel.length) {
13981             
13982             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13983
13984             cfg.cn = [
13985                 indicator,
13986                 {
13987                     tag: 'label',
13988                     'for' :  id,
13989                     cls : 'control-label',
13990                     html : this.fieldLabel
13991
13992                 },
13993                 {
13994                     cls : "", 
13995                     cn: [
13996                         combobox
13997                     ]
13998                 }
13999
14000             ];
14001             
14002             var labelCfg = cfg.cn[1];
14003             var contentCfg = cfg.cn[2];
14004             
14005             if(this.indicatorpos == 'right'){
14006                 cfg.cn = [
14007                     {
14008                         tag: 'label',
14009                         'for' :  id,
14010                         cls : 'control-label',
14011                         cn : [
14012                             {
14013                                 tag : 'span',
14014                                 html : this.fieldLabel
14015                             },
14016                             indicator
14017                         ]
14018                     },
14019                     {
14020                         cls : "", 
14021                         cn: [
14022                             combobox
14023                         ]
14024                     }
14025
14026                 ];
14027                 
14028                 labelCfg = cfg.cn[0];
14029                 contentCfg = cfg.cn[1];
14030             }
14031             
14032             if(this.labelWidth > 12){
14033                 labelCfg.style = "width: " + this.labelWidth + 'px';
14034             }
14035             
14036             if(this.labelWidth < 13 && this.labelmd == 0){
14037                 this.labelmd = this.labelWidth;
14038             }
14039             
14040             if(this.labellg > 0){
14041                 labelCfg.cls += ' col-lg-' + this.labellg;
14042                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14043             }
14044             
14045             if(this.labelmd > 0){
14046                 labelCfg.cls += ' col-md-' + this.labelmd;
14047                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14048             }
14049             
14050             if(this.labelsm > 0){
14051                 labelCfg.cls += ' col-sm-' + this.labelsm;
14052                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14053             }
14054             
14055             if(this.labelxs > 0){
14056                 labelCfg.cls += ' col-xs-' + this.labelxs;
14057                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14058             }
14059             
14060         } else if ( this.fieldLabel.length) {
14061 //                Roo.log(" label");
14062             cfg.cn = [
14063                 indicator,
14064                {
14065                    tag: 'label',
14066                    //cls : 'input-group-addon',
14067                    html : this.fieldLabel
14068
14069                },
14070
14071                combobox
14072
14073             ];
14074             
14075             if(this.indicatorpos == 'right'){
14076                 
14077                 cfg.cn = [
14078                     {
14079                        tag: 'label',
14080                        cn : [
14081                            {
14082                                tag : 'span',
14083                                html : this.fieldLabel
14084                            },
14085                            indicator
14086                        ]
14087
14088                     },
14089                     combobox
14090
14091                 ];
14092
14093             }
14094
14095         } else {
14096             
14097 //                Roo.log(" no label && no align");
14098                 cfg = combobox
14099                      
14100                 
14101         }
14102         
14103         var settings=this;
14104         ['xs','sm','md','lg'].map(function(size){
14105             if (settings[size]) {
14106                 cfg.cls += ' col-' + size + '-' + settings[size];
14107             }
14108         });
14109         
14110         return cfg;
14111         
14112     },
14113     
14114     
14115     
14116     // private
14117     onResize : function(w, h){
14118 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14119 //        if(typeof w == 'number'){
14120 //            var x = w - this.trigger.getWidth();
14121 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14122 //            this.trigger.setStyle('left', x+'px');
14123 //        }
14124     },
14125
14126     // private
14127     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14128
14129     // private
14130     getResizeEl : function(){
14131         return this.inputEl();
14132     },
14133
14134     // private
14135     getPositionEl : function(){
14136         return this.inputEl();
14137     },
14138
14139     // private
14140     alignErrorIcon : function(){
14141         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14142     },
14143
14144     // private
14145     initEvents : function(){
14146         
14147         this.createList();
14148         
14149         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14150         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14151         if(!this.multiple && this.showToggleBtn){
14152             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14153             if(this.hideTrigger){
14154                 this.trigger.setDisplayed(false);
14155             }
14156             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14157         }
14158         
14159         if(this.multiple){
14160             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14161         }
14162         
14163         if(this.removable && !this.editable && !this.tickable){
14164             var close = this.closeTriggerEl();
14165             
14166             if(close){
14167                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14168                 close.on('click', this.removeBtnClick, this, close);
14169             }
14170         }
14171         
14172         //this.trigger.addClassOnOver('x-form-trigger-over');
14173         //this.trigger.addClassOnClick('x-form-trigger-click');
14174         
14175         //if(!this.width){
14176         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14177         //}
14178     },
14179     
14180     closeTriggerEl : function()
14181     {
14182         var close = this.el.select('.roo-combo-removable-btn', true).first();
14183         return close ? close : false;
14184     },
14185     
14186     removeBtnClick : function(e, h, el)
14187     {
14188         e.preventDefault();
14189         
14190         if(this.fireEvent("remove", this) !== false){
14191             this.reset();
14192             this.fireEvent("afterremove", this)
14193         }
14194     },
14195     
14196     createList : function()
14197     {
14198         this.list = Roo.get(document.body).createChild({
14199             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14200             cls: 'typeahead typeahead-long dropdown-menu shadow',
14201             style: 'display:none'
14202         });
14203         
14204         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14205         
14206     },
14207
14208     // private
14209     initTrigger : function(){
14210        
14211     },
14212
14213     // private
14214     onDestroy : function(){
14215         if(this.trigger){
14216             this.trigger.removeAllListeners();
14217           //  this.trigger.remove();
14218         }
14219         //if(this.wrap){
14220         //    this.wrap.remove();
14221         //}
14222         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14223     },
14224
14225     // private
14226     onFocus : function(){
14227         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14228         /*
14229         if(!this.mimicing){
14230             this.wrap.addClass('x-trigger-wrap-focus');
14231             this.mimicing = true;
14232             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14233             if(this.monitorTab){
14234                 this.el.on("keydown", this.checkTab, this);
14235             }
14236         }
14237         */
14238     },
14239
14240     // private
14241     checkTab : function(e){
14242         if(e.getKey() == e.TAB){
14243             this.triggerBlur();
14244         }
14245     },
14246
14247     // private
14248     onBlur : function(){
14249         // do nothing
14250     },
14251
14252     // private
14253     mimicBlur : function(e, t){
14254         /*
14255         if(!this.wrap.contains(t) && this.validateBlur()){
14256             this.triggerBlur();
14257         }
14258         */
14259     },
14260
14261     // private
14262     triggerBlur : function(){
14263         this.mimicing = false;
14264         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14265         if(this.monitorTab){
14266             this.el.un("keydown", this.checkTab, this);
14267         }
14268         //this.wrap.removeClass('x-trigger-wrap-focus');
14269         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14270     },
14271
14272     // private
14273     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14274     validateBlur : function(e, t){
14275         return true;
14276     },
14277
14278     // private
14279     onDisable : function(){
14280         this.inputEl().dom.disabled = true;
14281         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14282         //if(this.wrap){
14283         //    this.wrap.addClass('x-item-disabled');
14284         //}
14285     },
14286
14287     // private
14288     onEnable : function(){
14289         this.inputEl().dom.disabled = false;
14290         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14291         //if(this.wrap){
14292         //    this.el.removeClass('x-item-disabled');
14293         //}
14294     },
14295
14296     // private
14297     onShow : function(){
14298         var ae = this.getActionEl();
14299         
14300         if(ae){
14301             ae.dom.style.display = '';
14302             ae.dom.style.visibility = 'visible';
14303         }
14304     },
14305
14306     // private
14307     
14308     onHide : function(){
14309         var ae = this.getActionEl();
14310         ae.dom.style.display = 'none';
14311     },
14312
14313     /**
14314      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14315      * by an implementing function.
14316      * @method
14317      * @param {EventObject} e
14318      */
14319     onTriggerClick : Roo.emptyFn
14320 });
14321  
14322 /*
14323 * Licence: LGPL
14324 */
14325
14326 /**
14327  * @class Roo.bootstrap.form.CardUploader
14328  * @extends Roo.bootstrap.Button
14329  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14330  * @cfg {Number} errorTimeout default 3000
14331  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14332  * @cfg {Array}  html The button text.
14333
14334  *
14335  * @constructor
14336  * Create a new CardUploader
14337  * @param {Object} config The config object
14338  */
14339
14340 Roo.bootstrap.form.CardUploader = function(config){
14341     
14342  
14343     
14344     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14345     
14346     
14347     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14348         return r.data.id
14349      });
14350     
14351      this.addEvents({
14352          // raw events
14353         /**
14354          * @event preview
14355          * When a image is clicked on - and needs to display a slideshow or similar..
14356          * @param {Roo.bootstrap.Card} this
14357          * @param {Object} The image information data 
14358          *
14359          */
14360         'preview' : true,
14361          /**
14362          * @event download
14363          * When a the download link is clicked
14364          * @param {Roo.bootstrap.Card} this
14365          * @param {Object} The image information data  contains 
14366          */
14367         'download' : true
14368         
14369     });
14370 };
14371  
14372 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14373     
14374      
14375     errorTimeout : 3000,
14376      
14377     images : false,
14378    
14379     fileCollection : false,
14380     allowBlank : true,
14381     
14382     getAutoCreate : function()
14383     {
14384         
14385         var cfg =  {
14386             cls :'form-group' ,
14387             cn : [
14388                
14389                 {
14390                     tag: 'label',
14391                    //cls : 'input-group-addon',
14392                     html : this.fieldLabel
14393
14394                 },
14395
14396                 {
14397                     tag: 'input',
14398                     type : 'hidden',
14399                     name : this.name,
14400                     value : this.value,
14401                     cls : 'd-none  form-control'
14402                 },
14403                 
14404                 {
14405                     tag: 'input',
14406                     multiple : 'multiple',
14407                     type : 'file',
14408                     cls : 'd-none  roo-card-upload-selector'
14409                 },
14410                 
14411                 {
14412                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14413                 },
14414                 {
14415                     cls : 'card-columns roo-card-uploader-container'
14416                 }
14417
14418             ]
14419         };
14420            
14421          
14422         return cfg;
14423     },
14424     
14425     getChildContainer : function() /// what children are added to.
14426     {
14427         return this.containerEl;
14428     },
14429    
14430     getButtonContainer : function() /// what children are added to.
14431     {
14432         return this.el.select(".roo-card-uploader-button-container").first();
14433     },
14434    
14435     initEvents : function()
14436     {
14437         
14438         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14439         
14440         var t = this;
14441         this.addxtype({
14442             xns: Roo.bootstrap,
14443
14444             xtype : 'Button',
14445             container_method : 'getButtonContainer' ,            
14446             html :  this.html, // fix changable?
14447             cls : 'w-100 ',
14448             listeners : {
14449                 'click' : function(btn, e) {
14450                     t.onClick(e);
14451                 }
14452             }
14453         });
14454         
14455         
14456         
14457         
14458         this.urlAPI = (window.createObjectURL && window) || 
14459                                 (window.URL && URL.revokeObjectURL && URL) || 
14460                                 (window.webkitURL && webkitURL);
14461                         
14462          
14463          
14464          
14465         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14466         
14467         this.selectorEl.on('change', this.onFileSelected, this);
14468         if (this.images) {
14469             var t = this;
14470             this.images.forEach(function(img) {
14471                 t.addCard(img)
14472             });
14473             this.images = false;
14474         }
14475         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14476          
14477        
14478     },
14479     
14480    
14481     onClick : function(e)
14482     {
14483         e.preventDefault();
14484          
14485         this.selectorEl.dom.click();
14486          
14487     },
14488     
14489     onFileSelected : function(e)
14490     {
14491         e.preventDefault();
14492         
14493         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14494             return;
14495         }
14496         
14497         Roo.each(this.selectorEl.dom.files, function(file){    
14498             this.addFile(file);
14499         }, this);
14500          
14501     },
14502     
14503       
14504     
14505       
14506     
14507     addFile : function(file)
14508     {
14509            
14510         if(typeof(file) === 'string'){
14511             throw "Add file by name?"; // should not happen
14512             return;
14513         }
14514         
14515         if(!file || !this.urlAPI){
14516             return;
14517         }
14518         
14519         // file;
14520         // file.type;
14521         
14522         var _this = this;
14523         
14524         
14525         var url = _this.urlAPI.createObjectURL( file);
14526            
14527         this.addCard({
14528             id : Roo.bootstrap.form.CardUploader.ID--,
14529             is_uploaded : false,
14530             src : url,
14531             srcfile : file,
14532             title : file.name,
14533             mimetype : file.type,
14534             preview : false,
14535             is_deleted : 0
14536         });
14537         
14538     },
14539     
14540     /**
14541      * addCard - add an Attachment to the uploader
14542      * @param data - the data about the image to upload
14543      *
14544      * {
14545           id : 123
14546           title : "Title of file",
14547           is_uploaded : false,
14548           src : "http://.....",
14549           srcfile : { the File upload object },
14550           mimetype : file.type,
14551           preview : false,
14552           is_deleted : 0
14553           .. any other data...
14554         }
14555      *
14556      * 
14557     */
14558     
14559     addCard : function (data)
14560     {
14561         // hidden input element?
14562         // if the file is not an image...
14563         //then we need to use something other that and header_image
14564         var t = this;
14565         //   remove.....
14566         var footer = [
14567             {
14568                 xns : Roo.bootstrap,
14569                 xtype : 'CardFooter',
14570                  items: [
14571                     {
14572                         xns : Roo.bootstrap,
14573                         xtype : 'Element',
14574                         cls : 'd-flex',
14575                         items : [
14576                             
14577                             {
14578                                 xns : Roo.bootstrap,
14579                                 xtype : 'Button',
14580                                 html : String.format("<small>{0}</small>", data.title),
14581                                 cls : 'col-10 text-left',
14582                                 size: 'sm',
14583                                 weight: 'link',
14584                                 fa : 'download',
14585                                 listeners : {
14586                                     click : function() {
14587                                      
14588                                         t.fireEvent( "download", t, data );
14589                                     }
14590                                 }
14591                             },
14592                           
14593                             {
14594                                 xns : Roo.bootstrap,
14595                                 xtype : 'Button',
14596                                 style: 'max-height: 28px; ',
14597                                 size : 'sm',
14598                                 weight: 'danger',
14599                                 cls : 'col-2',
14600                                 fa : 'times',
14601                                 listeners : {
14602                                     click : function() {
14603                                         t.removeCard(data.id)
14604                                     }
14605                                 }
14606                             }
14607                         ]
14608                     }
14609                     
14610                 ] 
14611             }
14612             
14613         ];
14614         
14615         var cn = this.addxtype(
14616             {
14617                  
14618                 xns : Roo.bootstrap,
14619                 xtype : 'Card',
14620                 closeable : true,
14621                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14622                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14623                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14624                 data : data,
14625                 html : false,
14626                  
14627                 items : footer,
14628                 initEvents : function() {
14629                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14630                     var card = this;
14631                     this.imgEl = this.el.select('.card-img-top').first();
14632                     if (this.imgEl) {
14633                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14634                         this.imgEl.set({ 'pointer' : 'cursor' });
14635                                   
14636                     }
14637                     this.getCardFooter().addClass('p-1');
14638                     
14639                   
14640                 }
14641                 
14642             }
14643         );
14644         // dont' really need ot update items.
14645         // this.items.push(cn);
14646         this.fileCollection.add(cn);
14647         
14648         if (!data.srcfile) {
14649             this.updateInput();
14650             return;
14651         }
14652             
14653         var _t = this;
14654         var reader = new FileReader();
14655         reader.addEventListener("load", function() {  
14656             data.srcdata =  reader.result;
14657             _t.updateInput();
14658         });
14659         reader.readAsDataURL(data.srcfile);
14660         
14661         
14662         
14663     },
14664     removeCard : function(id)
14665     {
14666         
14667         var card  = this.fileCollection.get(id);
14668         card.data.is_deleted = 1;
14669         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14670         //this.fileCollection.remove(card);
14671         //this.items = this.items.filter(function(e) { return e != card });
14672         // dont' really need ot update items.
14673         card.el.dom.parentNode.removeChild(card.el.dom);
14674         this.updateInput();
14675
14676         
14677     },
14678     reset: function()
14679     {
14680         this.fileCollection.each(function(card) {
14681             if (card.el.dom && card.el.dom.parentNode) {
14682                 card.el.dom.parentNode.removeChild(card.el.dom);
14683             }
14684         });
14685         this.fileCollection.clear();
14686         this.updateInput();
14687     },
14688     
14689     updateInput : function()
14690     {
14691          var data = [];
14692         this.fileCollection.each(function(e) {
14693             data.push(e.data);
14694             
14695         });
14696         this.inputEl().dom.value = JSON.stringify(data);
14697         
14698         
14699         
14700     }
14701     
14702     
14703 });
14704
14705
14706 Roo.bootstrap.form.CardUploader.ID = -1;/*
14707  * Based on:
14708  * Ext JS Library 1.1.1
14709  * Copyright(c) 2006-2007, Ext JS, LLC.
14710  *
14711  * Originally Released Under LGPL - original licence link has changed is not relivant.
14712  *
14713  * Fork - LGPL
14714  * <script type="text/javascript">
14715  */
14716
14717
14718 /**
14719  * @class Roo.data.SortTypes
14720  * @static
14721  * Defines the default sorting (casting?) comparison functions used when sorting data.
14722  */
14723 Roo.data.SortTypes = {
14724     /**
14725      * Default sort that does nothing
14726      * @param {Mixed} s The value being converted
14727      * @return {Mixed} The comparison value
14728      */
14729     none : function(s){
14730         return s;
14731     },
14732     
14733     /**
14734      * The regular expression used to strip tags
14735      * @type {RegExp}
14736      * @property
14737      */
14738     stripTagsRE : /<\/?[^>]+>/gi,
14739     
14740     /**
14741      * Strips all HTML tags to sort on text only
14742      * @param {Mixed} s The value being converted
14743      * @return {String} The comparison value
14744      */
14745     asText : function(s){
14746         return String(s).replace(this.stripTagsRE, "");
14747     },
14748     
14749     /**
14750      * Strips all HTML tags to sort on text only - Case insensitive
14751      * @param {Mixed} s The value being converted
14752      * @return {String} The comparison value
14753      */
14754     asUCText : function(s){
14755         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14756     },
14757     
14758     /**
14759      * Case insensitive string
14760      * @param {Mixed} s The value being converted
14761      * @return {String} The comparison value
14762      */
14763     asUCString : function(s) {
14764         return String(s).toUpperCase();
14765     },
14766     
14767     /**
14768      * Date sorting
14769      * @param {Mixed} s The value being converted
14770      * @return {Number} The comparison value
14771      */
14772     asDate : function(s) {
14773         if(!s){
14774             return 0;
14775         }
14776         if(s instanceof Date){
14777             return s.getTime();
14778         }
14779         return Date.parse(String(s));
14780     },
14781     
14782     /**
14783      * Float sorting
14784      * @param {Mixed} s The value being converted
14785      * @return {Float} The comparison value
14786      */
14787     asFloat : function(s) {
14788         var val = parseFloat(String(s).replace(/,/g, ""));
14789         if(isNaN(val)) {
14790             val = 0;
14791         }
14792         return val;
14793     },
14794     
14795     /**
14796      * Integer sorting
14797      * @param {Mixed} s The value being converted
14798      * @return {Number} The comparison value
14799      */
14800     asInt : function(s) {
14801         var val = parseInt(String(s).replace(/,/g, ""));
14802         if(isNaN(val)) {
14803             val = 0;
14804         }
14805         return val;
14806     }
14807 };/*
14808  * Based on:
14809  * Ext JS Library 1.1.1
14810  * Copyright(c) 2006-2007, Ext JS, LLC.
14811  *
14812  * Originally Released Under LGPL - original licence link has changed is not relivant.
14813  *
14814  * Fork - LGPL
14815  * <script type="text/javascript">
14816  */
14817
14818 /**
14819 * @class Roo.data.Record
14820  * Instances of this class encapsulate both record <em>definition</em> information, and record
14821  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14822  * to access Records cached in an {@link Roo.data.Store} object.<br>
14823  * <p>
14824  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14825  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14826  * objects.<br>
14827  * <p>
14828  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14829  * @constructor
14830  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14831  * {@link #create}. The parameters are the same.
14832  * @param {Array} data An associative Array of data values keyed by the field name.
14833  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14834  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14835  * not specified an integer id is generated.
14836  */
14837 Roo.data.Record = function(data, id){
14838     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14839     this.data = data;
14840 };
14841
14842 /**
14843  * Generate a constructor for a specific record layout.
14844  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14845  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14846  * Each field definition object may contain the following properties: <ul>
14847  * <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,
14848  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14849  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14850  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14851  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14852  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14853  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14854  * this may be omitted.</p></li>
14855  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14856  * <ul><li>auto (Default, implies no conversion)</li>
14857  * <li>string</li>
14858  * <li>int</li>
14859  * <li>float</li>
14860  * <li>boolean</li>
14861  * <li>date</li></ul></p></li>
14862  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14863  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14864  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14865  * by the Reader into an object that will be stored in the Record. It is passed the
14866  * following parameters:<ul>
14867  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14868  * </ul></p></li>
14869  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14870  * </ul>
14871  * <br>usage:<br><pre><code>
14872 var TopicRecord = Roo.data.Record.create(
14873     {name: 'title', mapping: 'topic_title'},
14874     {name: 'author', mapping: 'username'},
14875     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14876     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14877     {name: 'lastPoster', mapping: 'user2'},
14878     {name: 'excerpt', mapping: 'post_text'}
14879 );
14880
14881 var myNewRecord = new TopicRecord({
14882     title: 'Do my job please',
14883     author: 'noobie',
14884     totalPosts: 1,
14885     lastPost: new Date(),
14886     lastPoster: 'Animal',
14887     excerpt: 'No way dude!'
14888 });
14889 myStore.add(myNewRecord);
14890 </code></pre>
14891  * @method create
14892  * @static
14893  */
14894 Roo.data.Record.create = function(o){
14895     var f = function(){
14896         f.superclass.constructor.apply(this, arguments);
14897     };
14898     Roo.extend(f, Roo.data.Record);
14899     var p = f.prototype;
14900     p.fields = new Roo.util.MixedCollection(false, function(field){
14901         return field.name;
14902     });
14903     for(var i = 0, len = o.length; i < len; i++){
14904         p.fields.add(new Roo.data.Field(o[i]));
14905     }
14906     f.getField = function(name){
14907         return p.fields.get(name);  
14908     };
14909     return f;
14910 };
14911
14912 Roo.data.Record.AUTO_ID = 1000;
14913 Roo.data.Record.EDIT = 'edit';
14914 Roo.data.Record.REJECT = 'reject';
14915 Roo.data.Record.COMMIT = 'commit';
14916
14917 Roo.data.Record.prototype = {
14918     /**
14919      * Readonly flag - true if this record has been modified.
14920      * @type Boolean
14921      */
14922     dirty : false,
14923     editing : false,
14924     error: null,
14925     modified: null,
14926
14927     // private
14928     join : function(store){
14929         this.store = store;
14930     },
14931
14932     /**
14933      * Set the named field to the specified value.
14934      * @param {String} name The name of the field to set.
14935      * @param {Object} value The value to set the field to.
14936      */
14937     set : function(name, value){
14938         if(this.data[name] == value){
14939             return;
14940         }
14941         this.dirty = true;
14942         if(!this.modified){
14943             this.modified = {};
14944         }
14945         if(typeof this.modified[name] == 'undefined'){
14946             this.modified[name] = this.data[name];
14947         }
14948         this.data[name] = value;
14949         if(!this.editing && this.store){
14950             this.store.afterEdit(this);
14951         }       
14952     },
14953
14954     /**
14955      * Get the value of the named field.
14956      * @param {String} name The name of the field to get the value of.
14957      * @return {Object} The value of the field.
14958      */
14959     get : function(name){
14960         return this.data[name]; 
14961     },
14962
14963     // private
14964     beginEdit : function(){
14965         this.editing = true;
14966         this.modified = {}; 
14967     },
14968
14969     // private
14970     cancelEdit : function(){
14971         this.editing = false;
14972         delete this.modified;
14973     },
14974
14975     // private
14976     endEdit : function(){
14977         this.editing = false;
14978         if(this.dirty && this.store){
14979             this.store.afterEdit(this);
14980         }
14981     },
14982
14983     /**
14984      * Usually called by the {@link Roo.data.Store} which owns the Record.
14985      * Rejects all changes made to the Record since either creation, or the last commit operation.
14986      * Modified fields are reverted to their original values.
14987      * <p>
14988      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14989      * of reject operations.
14990      */
14991     reject : function(){
14992         var m = this.modified;
14993         for(var n in m){
14994             if(typeof m[n] != "function"){
14995                 this.data[n] = m[n];
14996             }
14997         }
14998         this.dirty = false;
14999         delete this.modified;
15000         this.editing = false;
15001         if(this.store){
15002             this.store.afterReject(this);
15003         }
15004     },
15005
15006     /**
15007      * Usually called by the {@link Roo.data.Store} which owns the Record.
15008      * Commits all changes made to the Record since either creation, or the last commit operation.
15009      * <p>
15010      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15011      * of commit operations.
15012      */
15013     commit : function(){
15014         this.dirty = false;
15015         delete this.modified;
15016         this.editing = false;
15017         if(this.store){
15018             this.store.afterCommit(this);
15019         }
15020     },
15021
15022     // private
15023     hasError : function(){
15024         return this.error != null;
15025     },
15026
15027     // private
15028     clearError : function(){
15029         this.error = null;
15030     },
15031
15032     /**
15033      * Creates a copy of this record.
15034      * @param {String} id (optional) A new record id if you don't want to use this record's id
15035      * @return {Record}
15036      */
15037     copy : function(newId) {
15038         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15039     }
15040 };/*
15041  * Based on:
15042  * Ext JS Library 1.1.1
15043  * Copyright(c) 2006-2007, Ext JS, LLC.
15044  *
15045  * Originally Released Under LGPL - original licence link has changed is not relivant.
15046  *
15047  * Fork - LGPL
15048  * <script type="text/javascript">
15049  */
15050
15051
15052
15053 /**
15054  * @class Roo.data.Store
15055  * @extends Roo.util.Observable
15056  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15057  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15058  * <p>
15059  * 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
15060  * has no knowledge of the format of the data returned by the Proxy.<br>
15061  * <p>
15062  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15063  * instances from the data object. These records are cached and made available through accessor functions.
15064  * @constructor
15065  * Creates a new Store.
15066  * @param {Object} config A config object containing the objects needed for the Store to access data,
15067  * and read the data into Records.
15068  */
15069 Roo.data.Store = function(config){
15070     this.data = new Roo.util.MixedCollection(false);
15071     this.data.getKey = function(o){
15072         return o.id;
15073     };
15074     this.baseParams = {};
15075     // private
15076     this.paramNames = {
15077         "start" : "start",
15078         "limit" : "limit",
15079         "sort" : "sort",
15080         "dir" : "dir",
15081         "multisort" : "_multisort"
15082     };
15083
15084     if(config && config.data){
15085         this.inlineData = config.data;
15086         delete config.data;
15087     }
15088
15089     Roo.apply(this, config);
15090     
15091     if(this.reader){ // reader passed
15092         this.reader = Roo.factory(this.reader, Roo.data);
15093         this.reader.xmodule = this.xmodule || false;
15094         if(!this.recordType){
15095             this.recordType = this.reader.recordType;
15096         }
15097         if(this.reader.onMetaChange){
15098             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15099         }
15100     }
15101
15102     if(this.recordType){
15103         this.fields = this.recordType.prototype.fields;
15104     }
15105     this.modified = [];
15106
15107     this.addEvents({
15108         /**
15109          * @event datachanged
15110          * Fires when the data cache has changed, and a widget which is using this Store
15111          * as a Record cache should refresh its view.
15112          * @param {Store} this
15113          */
15114         datachanged : true,
15115         /**
15116          * @event metachange
15117          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15118          * @param {Store} this
15119          * @param {Object} meta The JSON metadata
15120          */
15121         metachange : true,
15122         /**
15123          * @event add
15124          * Fires when Records have been added to the Store
15125          * @param {Store} this
15126          * @param {Roo.data.Record[]} records The array of Records added
15127          * @param {Number} index The index at which the record(s) were added
15128          */
15129         add : true,
15130         /**
15131          * @event remove
15132          * Fires when a Record has been removed from the Store
15133          * @param {Store} this
15134          * @param {Roo.data.Record} record The Record that was removed
15135          * @param {Number} index The index at which the record was removed
15136          */
15137         remove : true,
15138         /**
15139          * @event update
15140          * Fires when a Record has been updated
15141          * @param {Store} this
15142          * @param {Roo.data.Record} record The Record that was updated
15143          * @param {String} operation The update operation being performed.  Value may be one of:
15144          * <pre><code>
15145  Roo.data.Record.EDIT
15146  Roo.data.Record.REJECT
15147  Roo.data.Record.COMMIT
15148          * </code></pre>
15149          */
15150         update : true,
15151         /**
15152          * @event clear
15153          * Fires when the data cache has been cleared.
15154          * @param {Store} this
15155          */
15156         clear : true,
15157         /**
15158          * @event beforeload
15159          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15160          * the load action will be canceled.
15161          * @param {Store} this
15162          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15163          */
15164         beforeload : true,
15165         /**
15166          * @event beforeloadadd
15167          * Fires after a new set of Records has been loaded.
15168          * @param {Store} this
15169          * @param {Roo.data.Record[]} records The Records that were loaded
15170          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15171          */
15172         beforeloadadd : true,
15173         /**
15174          * @event load
15175          * Fires after a new set of Records has been loaded, before they are added to the store.
15176          * @param {Store} this
15177          * @param {Roo.data.Record[]} records The Records that were loaded
15178          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15179          * @params {Object} return from reader
15180          */
15181         load : true,
15182         /**
15183          * @event loadexception
15184          * Fires if an exception occurs in the Proxy during loading.
15185          * Called with the signature of the Proxy's "loadexception" event.
15186          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15187          * 
15188          * @param {Proxy} 
15189          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15190          * @param {Object} load options 
15191          * @param {Object} jsonData from your request (normally this contains the Exception)
15192          */
15193         loadexception : true
15194     });
15195     
15196     if(this.proxy){
15197         this.proxy = Roo.factory(this.proxy, Roo.data);
15198         this.proxy.xmodule = this.xmodule || false;
15199         this.relayEvents(this.proxy,  ["loadexception"]);
15200     }
15201     this.sortToggle = {};
15202     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15203
15204     Roo.data.Store.superclass.constructor.call(this);
15205
15206     if(this.inlineData){
15207         this.loadData(this.inlineData);
15208         delete this.inlineData;
15209     }
15210 };
15211
15212 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15213      /**
15214     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15215     * without a remote query - used by combo/forms at present.
15216     */
15217     
15218     /**
15219     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15220     */
15221     /**
15222     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15223     */
15224     /**
15225     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15226     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15227     */
15228     /**
15229     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15230     * on any HTTP request
15231     */
15232     /**
15233     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15234     */
15235     /**
15236     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15237     */
15238     multiSort: false,
15239     /**
15240     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15241     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15242     */
15243     remoteSort : false,
15244
15245     /**
15246     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15247      * loaded or when a record is removed. (defaults to false).
15248     */
15249     pruneModifiedRecords : false,
15250
15251     // private
15252     lastOptions : null,
15253
15254     /**
15255      * Add Records to the Store and fires the add event.
15256      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15257      */
15258     add : function(records){
15259         records = [].concat(records);
15260         for(var i = 0, len = records.length; i < len; i++){
15261             records[i].join(this);
15262         }
15263         var index = this.data.length;
15264         this.data.addAll(records);
15265         this.fireEvent("add", this, records, index);
15266     },
15267
15268     /**
15269      * Remove a Record from the Store and fires the remove event.
15270      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15271      */
15272     remove : function(record){
15273         var index = this.data.indexOf(record);
15274         this.data.removeAt(index);
15275  
15276         if(this.pruneModifiedRecords){
15277             this.modified.remove(record);
15278         }
15279         this.fireEvent("remove", this, record, index);
15280     },
15281
15282     /**
15283      * Remove all Records from the Store and fires the clear event.
15284      */
15285     removeAll : function(){
15286         this.data.clear();
15287         if(this.pruneModifiedRecords){
15288             this.modified = [];
15289         }
15290         this.fireEvent("clear", this);
15291     },
15292
15293     /**
15294      * Inserts Records to the Store at the given index and fires the add event.
15295      * @param {Number} index The start index at which to insert the passed Records.
15296      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15297      */
15298     insert : function(index, records){
15299         records = [].concat(records);
15300         for(var i = 0, len = records.length; i < len; i++){
15301             this.data.insert(index, records[i]);
15302             records[i].join(this);
15303         }
15304         this.fireEvent("add", this, records, index);
15305     },
15306
15307     /**
15308      * Get the index within the cache of the passed Record.
15309      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15310      * @return {Number} The index of the passed Record. Returns -1 if not found.
15311      */
15312     indexOf : function(record){
15313         return this.data.indexOf(record);
15314     },
15315
15316     /**
15317      * Get the index within the cache of the Record with the passed id.
15318      * @param {String} id The id of the Record to find.
15319      * @return {Number} The index of the Record. Returns -1 if not found.
15320      */
15321     indexOfId : function(id){
15322         return this.data.indexOfKey(id);
15323     },
15324
15325     /**
15326      * Get the Record with the specified id.
15327      * @param {String} id The id of the Record to find.
15328      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15329      */
15330     getById : function(id){
15331         return this.data.key(id);
15332     },
15333
15334     /**
15335      * Get the Record at the specified index.
15336      * @param {Number} index The index of the Record to find.
15337      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15338      */
15339     getAt : function(index){
15340         return this.data.itemAt(index);
15341     },
15342
15343     /**
15344      * Returns a range of Records between specified indices.
15345      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15346      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15347      * @return {Roo.data.Record[]} An array of Records
15348      */
15349     getRange : function(start, end){
15350         return this.data.getRange(start, end);
15351     },
15352
15353     // private
15354     storeOptions : function(o){
15355         o = Roo.apply({}, o);
15356         delete o.callback;
15357         delete o.scope;
15358         this.lastOptions = o;
15359     },
15360
15361     /**
15362      * Loads the Record cache from the configured Proxy using the configured Reader.
15363      * <p>
15364      * If using remote paging, then the first load call must specify the <em>start</em>
15365      * and <em>limit</em> properties in the options.params property to establish the initial
15366      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15367      * <p>
15368      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15369      * and this call will return before the new data has been loaded. Perform any post-processing
15370      * in a callback function, or in a "load" event handler.</strong>
15371      * <p>
15372      * @param {Object} options An object containing properties which control loading options:<ul>
15373      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15374      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15375      * <pre>
15376                 {
15377                     data : data,  // array of key=>value data like JsonReader
15378                     total : data.length,
15379                     success : true
15380                     
15381                 }
15382         </pre>
15383             }.</li>
15384      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15385      * passed the following arguments:<ul>
15386      * <li>r : Roo.data.Record[]</li>
15387      * <li>options: Options object from the load call</li>
15388      * <li>success: Boolean success indicator</li></ul></li>
15389      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15390      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15391      * </ul>
15392      */
15393     load : function(options){
15394         options = options || {};
15395         if(this.fireEvent("beforeload", this, options) !== false){
15396             this.storeOptions(options);
15397             var p = Roo.apply(options.params || {}, this.baseParams);
15398             // if meta was not loaded from remote source.. try requesting it.
15399             if (!this.reader.metaFromRemote) {
15400                 p._requestMeta = 1;
15401             }
15402             if(this.sortInfo && this.remoteSort){
15403                 var pn = this.paramNames;
15404                 p[pn["sort"]] = this.sortInfo.field;
15405                 p[pn["dir"]] = this.sortInfo.direction;
15406             }
15407             if (this.multiSort) {
15408                 var pn = this.paramNames;
15409                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15410             }
15411             
15412             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15413         }
15414     },
15415
15416     /**
15417      * Reloads the Record cache from the configured Proxy using the configured Reader and
15418      * the options from the last load operation performed.
15419      * @param {Object} options (optional) An object containing properties which may override the options
15420      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15421      * the most recently used options are reused).
15422      */
15423     reload : function(options){
15424         this.load(Roo.applyIf(options||{}, this.lastOptions));
15425     },
15426
15427     // private
15428     // Called as a callback by the Reader during a load operation.
15429     loadRecords : function(o, options, success){
15430          
15431         if(!o){
15432             if(success !== false){
15433                 this.fireEvent("load", this, [], options, o);
15434             }
15435             if(options.callback){
15436                 options.callback.call(options.scope || this, [], options, false);
15437             }
15438             return;
15439         }
15440         // if data returned failure - throw an exception.
15441         if (o.success === false) {
15442             // show a message if no listener is registered.
15443             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15444                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15445             }
15446             // loadmask wil be hooked into this..
15447             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15448             return;
15449         }
15450         var r = o.records, t = o.totalRecords || r.length;
15451         
15452         this.fireEvent("beforeloadadd", this, r, options, o);
15453         
15454         if(!options || options.add !== true){
15455             if(this.pruneModifiedRecords){
15456                 this.modified = [];
15457             }
15458             for(var i = 0, len = r.length; i < len; i++){
15459                 r[i].join(this);
15460             }
15461             if(this.snapshot){
15462                 this.data = this.snapshot;
15463                 delete this.snapshot;
15464             }
15465             this.data.clear();
15466             this.data.addAll(r);
15467             this.totalLength = t;
15468             this.applySort();
15469             this.fireEvent("datachanged", this);
15470         }else{
15471             this.totalLength = Math.max(t, this.data.length+r.length);
15472             this.add(r);
15473         }
15474         
15475         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15476                 
15477             var e = new Roo.data.Record({});
15478
15479             e.set(this.parent.displayField, this.parent.emptyTitle);
15480             e.set(this.parent.valueField, '');
15481
15482             this.insert(0, e);
15483         }
15484             
15485         this.fireEvent("load", this, r, options, o);
15486         if(options.callback){
15487             options.callback.call(options.scope || this, r, options, true);
15488         }
15489     },
15490
15491
15492     /**
15493      * Loads data from a passed data block. A Reader which understands the format of the data
15494      * must have been configured in the constructor.
15495      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15496      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15497      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15498      */
15499     loadData : function(o, append){
15500         var r = this.reader.readRecords(o);
15501         this.loadRecords(r, {add: append}, true);
15502     },
15503     
15504      /**
15505      * using 'cn' the nested child reader read the child array into it's child stores.
15506      * @param {Object} rec The record with a 'children array
15507      */
15508     loadDataFromChildren : function(rec)
15509     {
15510         this.loadData(this.reader.toLoadData(rec));
15511     },
15512     
15513
15514     /**
15515      * Gets the number of cached records.
15516      * <p>
15517      * <em>If using paging, this may not be the total size of the dataset. If the data object
15518      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15519      * the data set size</em>
15520      */
15521     getCount : function(){
15522         return this.data.length || 0;
15523     },
15524
15525     /**
15526      * Gets the total number of records in the dataset as returned by the server.
15527      * <p>
15528      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15529      * the dataset size</em>
15530      */
15531     getTotalCount : function(){
15532         return this.totalLength || 0;
15533     },
15534
15535     /**
15536      * Returns the sort state of the Store as an object with two properties:
15537      * <pre><code>
15538  field {String} The name of the field by which the Records are sorted
15539  direction {String} The sort order, "ASC" or "DESC"
15540      * </code></pre>
15541      */
15542     getSortState : function(){
15543         return this.sortInfo;
15544     },
15545
15546     // private
15547     applySort : function(){
15548         if(this.sortInfo && !this.remoteSort){
15549             var s = this.sortInfo, f = s.field;
15550             var st = this.fields.get(f).sortType;
15551             var fn = function(r1, r2){
15552                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15553                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15554             };
15555             this.data.sort(s.direction, fn);
15556             if(this.snapshot && this.snapshot != this.data){
15557                 this.snapshot.sort(s.direction, fn);
15558             }
15559         }
15560     },
15561
15562     /**
15563      * Sets the default sort column and order to be used by the next load operation.
15564      * @param {String} fieldName The name of the field to sort by.
15565      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15566      */
15567     setDefaultSort : function(field, dir){
15568         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15569     },
15570
15571     /**
15572      * Sort the Records.
15573      * If remote sorting is used, the sort is performed on the server, and the cache is
15574      * reloaded. If local sorting is used, the cache is sorted internally.
15575      * @param {String} fieldName The name of the field to sort by.
15576      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15577      */
15578     sort : function(fieldName, dir){
15579         var f = this.fields.get(fieldName);
15580         if(!dir){
15581             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15582             
15583             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15584                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15585             }else{
15586                 dir = f.sortDir;
15587             }
15588         }
15589         this.sortToggle[f.name] = dir;
15590         this.sortInfo = {field: f.name, direction: dir};
15591         if(!this.remoteSort){
15592             this.applySort();
15593             this.fireEvent("datachanged", this);
15594         }else{
15595             this.load(this.lastOptions);
15596         }
15597     },
15598
15599     /**
15600      * Calls the specified function for each of the Records in the cache.
15601      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15602      * Returning <em>false</em> aborts and exits the iteration.
15603      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15604      */
15605     each : function(fn, scope){
15606         this.data.each(fn, scope);
15607     },
15608
15609     /**
15610      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15611      * (e.g., during paging).
15612      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15613      */
15614     getModifiedRecords : function(){
15615         return this.modified;
15616     },
15617
15618     // private
15619     createFilterFn : function(property, value, anyMatch){
15620         if(!value.exec){ // not a regex
15621             value = String(value);
15622             if(value.length == 0){
15623                 return false;
15624             }
15625             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15626         }
15627         return function(r){
15628             return value.test(r.data[property]);
15629         };
15630     },
15631
15632     /**
15633      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15634      * @param {String} property A field on your records
15635      * @param {Number} start The record index to start at (defaults to 0)
15636      * @param {Number} end The last record index to include (defaults to length - 1)
15637      * @return {Number} The sum
15638      */
15639     sum : function(property, start, end){
15640         var rs = this.data.items, v = 0;
15641         start = start || 0;
15642         end = (end || end === 0) ? end : rs.length-1;
15643
15644         for(var i = start; i <= end; i++){
15645             v += (rs[i].data[property] || 0);
15646         }
15647         return v;
15648     },
15649
15650     /**
15651      * Filter the records by a specified property.
15652      * @param {String} field A field on your records
15653      * @param {String/RegExp} value Either a string that the field
15654      * should start with or a RegExp to test against the field
15655      * @param {Boolean} anyMatch True to match any part not just the beginning
15656      */
15657     filter : function(property, value, anyMatch){
15658         var fn = this.createFilterFn(property, value, anyMatch);
15659         return fn ? this.filterBy(fn) : this.clearFilter();
15660     },
15661
15662     /**
15663      * Filter by a function. The specified function will be called with each
15664      * record in this data source. If the function returns true the record is included,
15665      * otherwise it is filtered.
15666      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15667      * @param {Object} scope (optional) The scope of the function (defaults to this)
15668      */
15669     filterBy : function(fn, scope){
15670         this.snapshot = this.snapshot || this.data;
15671         this.data = this.queryBy(fn, scope||this);
15672         this.fireEvent("datachanged", this);
15673     },
15674
15675     /**
15676      * Query the records by a specified property.
15677      * @param {String} field A field on your records
15678      * @param {String/RegExp} value Either a string that the field
15679      * should start with or a RegExp to test against the field
15680      * @param {Boolean} anyMatch True to match any part not just the beginning
15681      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15682      */
15683     query : function(property, value, anyMatch){
15684         var fn = this.createFilterFn(property, value, anyMatch);
15685         return fn ? this.queryBy(fn) : this.data.clone();
15686     },
15687
15688     /**
15689      * Query by a function. The specified function will be called with each
15690      * record in this data source. If the function returns true the record is included
15691      * in the results.
15692      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15693      * @param {Object} scope (optional) The scope of the function (defaults to this)
15694       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15695      **/
15696     queryBy : function(fn, scope){
15697         var data = this.snapshot || this.data;
15698         return data.filterBy(fn, scope||this);
15699     },
15700
15701     /**
15702      * Collects unique values for a particular dataIndex from this store.
15703      * @param {String} dataIndex The property to collect
15704      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15705      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15706      * @return {Array} An array of the unique values
15707      **/
15708     collect : function(dataIndex, allowNull, bypassFilter){
15709         var d = (bypassFilter === true && this.snapshot) ?
15710                 this.snapshot.items : this.data.items;
15711         var v, sv, r = [], l = {};
15712         for(var i = 0, len = d.length; i < len; i++){
15713             v = d[i].data[dataIndex];
15714             sv = String(v);
15715             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15716                 l[sv] = true;
15717                 r[r.length] = v;
15718             }
15719         }
15720         return r;
15721     },
15722
15723     /**
15724      * Revert to a view of the Record cache with no filtering applied.
15725      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15726      */
15727     clearFilter : function(suppressEvent){
15728         if(this.snapshot && this.snapshot != this.data){
15729             this.data = this.snapshot;
15730             delete this.snapshot;
15731             if(suppressEvent !== true){
15732                 this.fireEvent("datachanged", this);
15733             }
15734         }
15735     },
15736
15737     // private
15738     afterEdit : function(record){
15739         if(this.modified.indexOf(record) == -1){
15740             this.modified.push(record);
15741         }
15742         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15743     },
15744     
15745     // private
15746     afterReject : function(record){
15747         this.modified.remove(record);
15748         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15749     },
15750
15751     // private
15752     afterCommit : function(record){
15753         this.modified.remove(record);
15754         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15755     },
15756
15757     /**
15758      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15759      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15760      */
15761     commitChanges : function(){
15762         var m = this.modified.slice(0);
15763         this.modified = [];
15764         for(var i = 0, len = m.length; i < len; i++){
15765             m[i].commit();
15766         }
15767     },
15768
15769     /**
15770      * Cancel outstanding changes on all changed records.
15771      */
15772     rejectChanges : function(){
15773         var m = this.modified.slice(0);
15774         this.modified = [];
15775         for(var i = 0, len = m.length; i < len; i++){
15776             m[i].reject();
15777         }
15778     },
15779
15780     onMetaChange : function(meta, rtype, o){
15781         this.recordType = rtype;
15782         this.fields = rtype.prototype.fields;
15783         delete this.snapshot;
15784         this.sortInfo = meta.sortInfo || this.sortInfo;
15785         this.modified = [];
15786         this.fireEvent('metachange', this, this.reader.meta);
15787     },
15788     
15789     moveIndex : function(data, type)
15790     {
15791         var index = this.indexOf(data);
15792         
15793         var newIndex = index + type;
15794         
15795         this.remove(data);
15796         
15797         this.insert(newIndex, data);
15798         
15799     }
15800 });/*
15801  * Based on:
15802  * Ext JS Library 1.1.1
15803  * Copyright(c) 2006-2007, Ext JS, LLC.
15804  *
15805  * Originally Released Under LGPL - original licence link has changed is not relivant.
15806  *
15807  * Fork - LGPL
15808  * <script type="text/javascript">
15809  */
15810
15811 /**
15812  * @class Roo.data.SimpleStore
15813  * @extends Roo.data.Store
15814  * Small helper class to make creating Stores from Array data easier.
15815  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15816  * @cfg {Array} fields An array of field definition objects, or field name strings.
15817  * @cfg {Object} an existing reader (eg. copied from another store)
15818  * @cfg {Array} data The multi-dimensional array of data
15819  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15820  * @cfg {Roo.data.Reader} reader  [not-required] 
15821  * @constructor
15822  * @param {Object} config
15823  */
15824 Roo.data.SimpleStore = function(config)
15825 {
15826     Roo.data.SimpleStore.superclass.constructor.call(this, {
15827         isLocal : true,
15828         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15829                 id: config.id
15830             },
15831             Roo.data.Record.create(config.fields)
15832         ),
15833         proxy : new Roo.data.MemoryProxy(config.data)
15834     });
15835     this.load();
15836 };
15837 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15838  * Based on:
15839  * Ext JS Library 1.1.1
15840  * Copyright(c) 2006-2007, Ext JS, LLC.
15841  *
15842  * Originally Released Under LGPL - original licence link has changed is not relivant.
15843  *
15844  * Fork - LGPL
15845  * <script type="text/javascript">
15846  */
15847
15848 /**
15849 /**
15850  * @extends Roo.data.Store
15851  * @class Roo.data.JsonStore
15852  * Small helper class to make creating Stores for JSON data easier. <br/>
15853 <pre><code>
15854 var store = new Roo.data.JsonStore({
15855     url: 'get-images.php',
15856     root: 'images',
15857     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15858 });
15859 </code></pre>
15860  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15861  * JsonReader and HttpProxy (unless inline data is provided).</b>
15862  * @cfg {Array} fields An array of field definition objects, or field name strings.
15863  * @constructor
15864  * @param {Object} config
15865  */
15866 Roo.data.JsonStore = function(c){
15867     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15868         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15869         reader: new Roo.data.JsonReader(c, c.fields)
15870     }));
15871 };
15872 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15873  * Based on:
15874  * Ext JS Library 1.1.1
15875  * Copyright(c) 2006-2007, Ext JS, LLC.
15876  *
15877  * Originally Released Under LGPL - original licence link has changed is not relivant.
15878  *
15879  * Fork - LGPL
15880  * <script type="text/javascript">
15881  */
15882
15883  
15884 Roo.data.Field = function(config){
15885     if(typeof config == "string"){
15886         config = {name: config};
15887     }
15888     Roo.apply(this, config);
15889     
15890     if(!this.type){
15891         this.type = "auto";
15892     }
15893     
15894     var st = Roo.data.SortTypes;
15895     // named sortTypes are supported, here we look them up
15896     if(typeof this.sortType == "string"){
15897         this.sortType = st[this.sortType];
15898     }
15899     
15900     // set default sortType for strings and dates
15901     if(!this.sortType){
15902         switch(this.type){
15903             case "string":
15904                 this.sortType = st.asUCString;
15905                 break;
15906             case "date":
15907                 this.sortType = st.asDate;
15908                 break;
15909             default:
15910                 this.sortType = st.none;
15911         }
15912     }
15913
15914     // define once
15915     var stripRe = /[\$,%]/g;
15916
15917     // prebuilt conversion function for this field, instead of
15918     // switching every time we're reading a value
15919     if(!this.convert){
15920         var cv, dateFormat = this.dateFormat;
15921         switch(this.type){
15922             case "":
15923             case "auto":
15924             case undefined:
15925                 cv = function(v){ return v; };
15926                 break;
15927             case "string":
15928                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15929                 break;
15930             case "int":
15931                 cv = function(v){
15932                     return v !== undefined && v !== null && v !== '' ?
15933                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15934                     };
15935                 break;
15936             case "float":
15937                 cv = function(v){
15938                     return v !== undefined && v !== null && v !== '' ?
15939                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15940                     };
15941                 break;
15942             case "bool":
15943             case "boolean":
15944                 cv = function(v){ return v === true || v === "true" || v == 1; };
15945                 break;
15946             case "date":
15947                 cv = function(v){
15948                     if(!v){
15949                         return '';
15950                     }
15951                     if(v instanceof Date){
15952                         return v;
15953                     }
15954                     if(dateFormat){
15955                         if(dateFormat == "timestamp"){
15956                             return new Date(v*1000);
15957                         }
15958                         return Date.parseDate(v, dateFormat);
15959                     }
15960                     var parsed = Date.parse(v);
15961                     return parsed ? new Date(parsed) : null;
15962                 };
15963              break;
15964             
15965         }
15966         this.convert = cv;
15967     }
15968 };
15969
15970 Roo.data.Field.prototype = {
15971     dateFormat: null,
15972     defaultValue: "",
15973     mapping: null,
15974     sortType : null,
15975     sortDir : "ASC"
15976 };/*
15977  * Based on:
15978  * Ext JS Library 1.1.1
15979  * Copyright(c) 2006-2007, Ext JS, LLC.
15980  *
15981  * Originally Released Under LGPL - original licence link has changed is not relivant.
15982  *
15983  * Fork - LGPL
15984  * <script type="text/javascript">
15985  */
15986  
15987 // Base class for reading structured data from a data source.  This class is intended to be
15988 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15989
15990 /**
15991  * @class Roo.data.DataReader
15992  * @abstract
15993  * Base class for reading structured data from a data source.  This class is intended to be
15994  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15995  */
15996
15997 Roo.data.DataReader = function(meta, recordType){
15998     
15999     this.meta = meta;
16000     
16001     this.recordType = recordType instanceof Array ? 
16002         Roo.data.Record.create(recordType) : recordType;
16003 };
16004
16005 Roo.data.DataReader.prototype = {
16006     
16007     
16008     readerType : 'Data',
16009      /**
16010      * Create an empty record
16011      * @param {Object} data (optional) - overlay some values
16012      * @return {Roo.data.Record} record created.
16013      */
16014     newRow :  function(d) {
16015         var da =  {};
16016         this.recordType.prototype.fields.each(function(c) {
16017             switch( c.type) {
16018                 case 'int' : da[c.name] = 0; break;
16019                 case 'date' : da[c.name] = new Date(); break;
16020                 case 'float' : da[c.name] = 0.0; break;
16021                 case 'boolean' : da[c.name] = false; break;
16022                 default : da[c.name] = ""; break;
16023             }
16024             
16025         });
16026         return new this.recordType(Roo.apply(da, d));
16027     }
16028     
16029     
16030 };/*
16031  * Based on:
16032  * Ext JS Library 1.1.1
16033  * Copyright(c) 2006-2007, Ext JS, LLC.
16034  *
16035  * Originally Released Under LGPL - original licence link has changed is not relivant.
16036  *
16037  * Fork - LGPL
16038  * <script type="text/javascript">
16039  */
16040
16041 /**
16042  * @class Roo.data.DataProxy
16043  * @extends Roo.util.Observable
16044  * @abstract
16045  * This class is an abstract base class for implementations which provide retrieval of
16046  * unformatted data objects.<br>
16047  * <p>
16048  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16049  * (of the appropriate type which knows how to parse the data object) to provide a block of
16050  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16051  * <p>
16052  * Custom implementations must implement the load method as described in
16053  * {@link Roo.data.HttpProxy#load}.
16054  */
16055 Roo.data.DataProxy = function(){
16056     this.addEvents({
16057         /**
16058          * @event beforeload
16059          * Fires before a network request is made to retrieve a data object.
16060          * @param {Object} This DataProxy object.
16061          * @param {Object} params The params parameter to the load function.
16062          */
16063         beforeload : true,
16064         /**
16065          * @event load
16066          * Fires before the load method's callback is called.
16067          * @param {Object} This DataProxy object.
16068          * @param {Object} o The data object.
16069          * @param {Object} arg The callback argument object passed to the load function.
16070          */
16071         load : true,
16072         /**
16073          * @event loadexception
16074          * Fires if an Exception occurs during data retrieval.
16075          * @param {Object} This DataProxy object.
16076          * @param {Object} o The data object.
16077          * @param {Object} arg The callback argument object passed to the load function.
16078          * @param {Object} e The Exception.
16079          */
16080         loadexception : true
16081     });
16082     Roo.data.DataProxy.superclass.constructor.call(this);
16083 };
16084
16085 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16086
16087     /**
16088      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16089      */
16090 /*
16091  * Based on:
16092  * Ext JS Library 1.1.1
16093  * Copyright(c) 2006-2007, Ext JS, LLC.
16094  *
16095  * Originally Released Under LGPL - original licence link has changed is not relivant.
16096  *
16097  * Fork - LGPL
16098  * <script type="text/javascript">
16099  */
16100 /**
16101  * @class Roo.data.MemoryProxy
16102  * @extends Roo.data.DataProxy
16103  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16104  * to the Reader when its load method is called.
16105  * @constructor
16106  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16107  */
16108 Roo.data.MemoryProxy = function(config){
16109     var data = config;
16110     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16111         data = config.data;
16112     }
16113     Roo.data.MemoryProxy.superclass.constructor.call(this);
16114     this.data = data;
16115 };
16116
16117 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16118     
16119     /**
16120      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16121      */
16122     /**
16123      * Load data from the requested source (in this case an in-memory
16124      * data object passed to the constructor), read the data object into
16125      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16126      * process that block using the passed callback.
16127      * @param {Object} params This parameter is not used by the MemoryProxy class.
16128      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16129      * object into a block of Roo.data.Records.
16130      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16131      * The function must be passed <ul>
16132      * <li>The Record block object</li>
16133      * <li>The "arg" argument from the load function</li>
16134      * <li>A boolean success indicator</li>
16135      * </ul>
16136      * @param {Object} scope The scope in which to call the callback
16137      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16138      */
16139     load : function(params, reader, callback, scope, arg){
16140         params = params || {};
16141         var result;
16142         try {
16143             result = reader.readRecords(params.data ? params.data :this.data);
16144         }catch(e){
16145             this.fireEvent("loadexception", this, arg, null, e);
16146             callback.call(scope, null, arg, false);
16147             return;
16148         }
16149         callback.call(scope, result, arg, true);
16150     },
16151     
16152     // private
16153     update : function(params, records){
16154         
16155     }
16156 });/*
16157  * Based on:
16158  * Ext JS Library 1.1.1
16159  * Copyright(c) 2006-2007, Ext JS, LLC.
16160  *
16161  * Originally Released Under LGPL - original licence link has changed is not relivant.
16162  *
16163  * Fork - LGPL
16164  * <script type="text/javascript">
16165  */
16166 /**
16167  * @class Roo.data.HttpProxy
16168  * @extends Roo.data.DataProxy
16169  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16170  * configured to reference a certain URL.<br><br>
16171  * <p>
16172  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16173  * from which the running page was served.<br><br>
16174  * <p>
16175  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16176  * <p>
16177  * Be aware that to enable the browser to parse an XML document, the server must set
16178  * the Content-Type header in the HTTP response to "text/xml".
16179  * @constructor
16180  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16181  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16182  * will be used to make the request.
16183  */
16184 Roo.data.HttpProxy = function(conn){
16185     Roo.data.HttpProxy.superclass.constructor.call(this);
16186     // is conn a conn config or a real conn?
16187     this.conn = conn;
16188     this.useAjax = !conn || !conn.events;
16189   
16190 };
16191
16192 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16193     // thse are take from connection...
16194     
16195     /**
16196      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16197      */
16198     /**
16199      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16200      * extra parameters to each request made by this object. (defaults to undefined)
16201      */
16202     /**
16203      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16204      *  to each request made by this object. (defaults to undefined)
16205      */
16206     /**
16207      * @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)
16208      */
16209     /**
16210      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16211      */
16212      /**
16213      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16214      * @type Boolean
16215      */
16216   
16217
16218     /**
16219      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16220      * @type Boolean
16221      */
16222     /**
16223      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16224      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16225      * a finer-grained basis than the DataProxy events.
16226      */
16227     getConnection : function(){
16228         return this.useAjax ? Roo.Ajax : this.conn;
16229     },
16230
16231     /**
16232      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16233      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16234      * process that block using the passed callback.
16235      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16236      * for the request to the remote server.
16237      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16238      * object into a block of Roo.data.Records.
16239      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16240      * The function must be passed <ul>
16241      * <li>The Record block object</li>
16242      * <li>The "arg" argument from the load function</li>
16243      * <li>A boolean success indicator</li>
16244      * </ul>
16245      * @param {Object} scope The scope in which to call the callback
16246      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16247      */
16248     load : function(params, reader, callback, scope, arg){
16249         if(this.fireEvent("beforeload", this, params) !== false){
16250             var  o = {
16251                 params : params || {},
16252                 request: {
16253                     callback : callback,
16254                     scope : scope,
16255                     arg : arg
16256                 },
16257                 reader: reader,
16258                 callback : this.loadResponse,
16259                 scope: this
16260             };
16261             if(this.useAjax){
16262                 Roo.applyIf(o, this.conn);
16263                 if(this.activeRequest){
16264                     Roo.Ajax.abort(this.activeRequest);
16265                 }
16266                 this.activeRequest = Roo.Ajax.request(o);
16267             }else{
16268                 this.conn.request(o);
16269             }
16270         }else{
16271             callback.call(scope||this, null, arg, false);
16272         }
16273     },
16274
16275     // private
16276     loadResponse : function(o, success, response){
16277         delete this.activeRequest;
16278         if(!success){
16279             this.fireEvent("loadexception", this, o, response);
16280             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16281             return;
16282         }
16283         var result;
16284         try {
16285             result = o.reader.read(response);
16286         }catch(e){
16287             o.success = false;
16288             o.raw = { errorMsg : response.responseText };
16289             this.fireEvent("loadexception", this, o, response, e);
16290             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16291             return;
16292         }
16293         
16294         this.fireEvent("load", this, o, o.request.arg);
16295         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16296     },
16297
16298     // private
16299     update : function(dataSet){
16300
16301     },
16302
16303     // private
16304     updateResponse : function(dataSet){
16305
16306     }
16307 });/*
16308  * Based on:
16309  * Ext JS Library 1.1.1
16310  * Copyright(c) 2006-2007, Ext JS, LLC.
16311  *
16312  * Originally Released Under LGPL - original licence link has changed is not relivant.
16313  *
16314  * Fork - LGPL
16315  * <script type="text/javascript">
16316  */
16317
16318 /**
16319  * @class Roo.data.ScriptTagProxy
16320  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16321  * other than the originating domain of the running page.<br><br>
16322  * <p>
16323  * <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
16324  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16325  * <p>
16326  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16327  * source code that is used as the source inside a &lt;script> tag.<br><br>
16328  * <p>
16329  * In order for the browser to process the returned data, the server must wrap the data object
16330  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16331  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16332  * depending on whether the callback name was passed:
16333  * <p>
16334  * <pre><code>
16335 boolean scriptTag = false;
16336 String cb = request.getParameter("callback");
16337 if (cb != null) {
16338     scriptTag = true;
16339     response.setContentType("text/javascript");
16340 } else {
16341     response.setContentType("application/x-json");
16342 }
16343 Writer out = response.getWriter();
16344 if (scriptTag) {
16345     out.write(cb + "(");
16346 }
16347 out.print(dataBlock.toJsonString());
16348 if (scriptTag) {
16349     out.write(");");
16350 }
16351 </pre></code>
16352  *
16353  * @constructor
16354  * @param {Object} config A configuration object.
16355  */
16356 Roo.data.ScriptTagProxy = function(config){
16357     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16358     Roo.apply(this, config);
16359     this.head = document.getElementsByTagName("head")[0];
16360 };
16361
16362 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16363
16364 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16365     /**
16366      * @cfg {String} url The URL from which to request the data object.
16367      */
16368     /**
16369      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16370      */
16371     timeout : 30000,
16372     /**
16373      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16374      * the server the name of the callback function set up by the load call to process the returned data object.
16375      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16376      * javascript output which calls this named function passing the data object as its only parameter.
16377      */
16378     callbackParam : "callback",
16379     /**
16380      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16381      * name to the request.
16382      */
16383     nocache : true,
16384
16385     /**
16386      * Load data from the configured URL, read the data object into
16387      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16388      * process that block using the passed callback.
16389      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16390      * for the request to the remote server.
16391      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16392      * object into a block of Roo.data.Records.
16393      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16394      * The function must be passed <ul>
16395      * <li>The Record block object</li>
16396      * <li>The "arg" argument from the load function</li>
16397      * <li>A boolean success indicator</li>
16398      * </ul>
16399      * @param {Object} scope The scope in which to call the callback
16400      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16401      */
16402     load : function(params, reader, callback, scope, arg){
16403         if(this.fireEvent("beforeload", this, params) !== false){
16404
16405             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16406
16407             var url = this.url;
16408             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16409             if(this.nocache){
16410                 url += "&_dc=" + (new Date().getTime());
16411             }
16412             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16413             var trans = {
16414                 id : transId,
16415                 cb : "stcCallback"+transId,
16416                 scriptId : "stcScript"+transId,
16417                 params : params,
16418                 arg : arg,
16419                 url : url,
16420                 callback : callback,
16421                 scope : scope,
16422                 reader : reader
16423             };
16424             var conn = this;
16425
16426             window[trans.cb] = function(o){
16427                 conn.handleResponse(o, trans);
16428             };
16429
16430             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16431
16432             if(this.autoAbort !== false){
16433                 this.abort();
16434             }
16435
16436             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16437
16438             var script = document.createElement("script");
16439             script.setAttribute("src", url);
16440             script.setAttribute("type", "text/javascript");
16441             script.setAttribute("id", trans.scriptId);
16442             this.head.appendChild(script);
16443
16444             this.trans = trans;
16445         }else{
16446             callback.call(scope||this, null, arg, false);
16447         }
16448     },
16449
16450     // private
16451     isLoading : function(){
16452         return this.trans ? true : false;
16453     },
16454
16455     /**
16456      * Abort the current server request.
16457      */
16458     abort : function(){
16459         if(this.isLoading()){
16460             this.destroyTrans(this.trans);
16461         }
16462     },
16463
16464     // private
16465     destroyTrans : function(trans, isLoaded){
16466         this.head.removeChild(document.getElementById(trans.scriptId));
16467         clearTimeout(trans.timeoutId);
16468         if(isLoaded){
16469             window[trans.cb] = undefined;
16470             try{
16471                 delete window[trans.cb];
16472             }catch(e){}
16473         }else{
16474             // if hasn't been loaded, wait for load to remove it to prevent script error
16475             window[trans.cb] = function(){
16476                 window[trans.cb] = undefined;
16477                 try{
16478                     delete window[trans.cb];
16479                 }catch(e){}
16480             };
16481         }
16482     },
16483
16484     // private
16485     handleResponse : function(o, trans){
16486         this.trans = false;
16487         this.destroyTrans(trans, true);
16488         var result;
16489         try {
16490             result = trans.reader.readRecords(o);
16491         }catch(e){
16492             this.fireEvent("loadexception", this, o, trans.arg, e);
16493             trans.callback.call(trans.scope||window, null, trans.arg, false);
16494             return;
16495         }
16496         this.fireEvent("load", this, o, trans.arg);
16497         trans.callback.call(trans.scope||window, result, trans.arg, true);
16498     },
16499
16500     // private
16501     handleFailure : function(trans){
16502         this.trans = false;
16503         this.destroyTrans(trans, false);
16504         this.fireEvent("loadexception", this, null, trans.arg);
16505         trans.callback.call(trans.scope||window, null, trans.arg, false);
16506     }
16507 });/*
16508  * Based on:
16509  * Ext JS Library 1.1.1
16510  * Copyright(c) 2006-2007, Ext JS, LLC.
16511  *
16512  * Originally Released Under LGPL - original licence link has changed is not relivant.
16513  *
16514  * Fork - LGPL
16515  * <script type="text/javascript">
16516  */
16517
16518 /**
16519  * @class Roo.data.JsonReader
16520  * @extends Roo.data.DataReader
16521  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16522  * based on mappings in a provided Roo.data.Record constructor.
16523  * 
16524  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16525  * in the reply previously. 
16526  * 
16527  * <p>
16528  * Example code:
16529  * <pre><code>
16530 var RecordDef = Roo.data.Record.create([
16531     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16532     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16533 ]);
16534 var myReader = new Roo.data.JsonReader({
16535     totalProperty: "results",    // The property which contains the total dataset size (optional)
16536     root: "rows",                // The property which contains an Array of row objects
16537     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16538 }, RecordDef);
16539 </code></pre>
16540  * <p>
16541  * This would consume a JSON file like this:
16542  * <pre><code>
16543 { 'results': 2, 'rows': [
16544     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16545     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16546 }
16547 </code></pre>
16548  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16549  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16550  * paged from the remote server.
16551  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16552  * @cfg {String} root name of the property which contains the Array of row objects.
16553  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16554  * @cfg {Array} fields Array of field definition objects
16555  * @constructor
16556  * Create a new JsonReader
16557  * @param {Object} meta Metadata configuration options
16558  * @param {Object} recordType Either an Array of field definition objects,
16559  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16560  */
16561 Roo.data.JsonReader = function(meta, recordType){
16562     
16563     meta = meta || {};
16564     // set some defaults:
16565     Roo.applyIf(meta, {
16566         totalProperty: 'total',
16567         successProperty : 'success',
16568         root : 'data',
16569         id : 'id'
16570     });
16571     
16572     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16573 };
16574 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16575     
16576     readerType : 'Json',
16577     
16578     /**
16579      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16580      * Used by Store query builder to append _requestMeta to params.
16581      * 
16582      */
16583     metaFromRemote : false,
16584     /**
16585      * This method is only used by a DataProxy which has retrieved data from a remote server.
16586      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16587      * @return {Object} data A data block which is used by an Roo.data.Store object as
16588      * a cache of Roo.data.Records.
16589      */
16590     read : function(response){
16591         var json = response.responseText;
16592        
16593         var o = /* eval:var:o */ eval("("+json+")");
16594         if(!o) {
16595             throw {message: "JsonReader.read: Json object not found"};
16596         }
16597         
16598         if(o.metaData){
16599             
16600             delete this.ef;
16601             this.metaFromRemote = true;
16602             this.meta = o.metaData;
16603             this.recordType = Roo.data.Record.create(o.metaData.fields);
16604             this.onMetaChange(this.meta, this.recordType, o);
16605         }
16606         return this.readRecords(o);
16607     },
16608
16609     // private function a store will implement
16610     onMetaChange : function(meta, recordType, o){
16611
16612     },
16613
16614     /**
16615          * @ignore
16616          */
16617     simpleAccess: function(obj, subsc) {
16618         return obj[subsc];
16619     },
16620
16621         /**
16622          * @ignore
16623          */
16624     getJsonAccessor: function(){
16625         var re = /[\[\.]/;
16626         return function(expr) {
16627             try {
16628                 return(re.test(expr))
16629                     ? new Function("obj", "return obj." + expr)
16630                     : function(obj){
16631                         return obj[expr];
16632                     };
16633             } catch(e){}
16634             return Roo.emptyFn;
16635         };
16636     }(),
16637
16638     /**
16639      * Create a data block containing Roo.data.Records from an XML document.
16640      * @param {Object} o An object which contains an Array of row objects in the property specified
16641      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16642      * which contains the total size of the dataset.
16643      * @return {Object} data A data block which is used by an Roo.data.Store object as
16644      * a cache of Roo.data.Records.
16645      */
16646     readRecords : function(o){
16647         /**
16648          * After any data loads, the raw JSON data is available for further custom processing.
16649          * @type Object
16650          */
16651         this.o = o;
16652         var s = this.meta, Record = this.recordType,
16653             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16654
16655 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16656         if (!this.ef) {
16657             if(s.totalProperty) {
16658                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16659                 }
16660                 if(s.successProperty) {
16661                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16662                 }
16663                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16664                 if (s.id) {
16665                         var g = this.getJsonAccessor(s.id);
16666                         this.getId = function(rec) {
16667                                 var r = g(rec);  
16668                                 return (r === undefined || r === "") ? null : r;
16669                         };
16670                 } else {
16671                         this.getId = function(){return null;};
16672                 }
16673             this.ef = [];
16674             for(var jj = 0; jj < fl; jj++){
16675                 f = fi[jj];
16676                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16677                 this.ef[jj] = this.getJsonAccessor(map);
16678             }
16679         }
16680
16681         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16682         if(s.totalProperty){
16683             var vt = parseInt(this.getTotal(o), 10);
16684             if(!isNaN(vt)){
16685                 totalRecords = vt;
16686             }
16687         }
16688         if(s.successProperty){
16689             var vs = this.getSuccess(o);
16690             if(vs === false || vs === 'false'){
16691                 success = false;
16692             }
16693         }
16694         var records = [];
16695         for(var i = 0; i < c; i++){
16696             var n = root[i];
16697             var values = {};
16698             var id = this.getId(n);
16699             for(var j = 0; j < fl; j++){
16700                 f = fi[j];
16701                                 var v = this.ef[j](n);
16702                                 if (!f.convert) {
16703                                         Roo.log('missing convert for ' + f.name);
16704                                         Roo.log(f);
16705                                         continue;
16706                                 }
16707                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16708             }
16709                         if (!Record) {
16710                                 return {
16711                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16712                                         success : false,
16713                                         records : [],
16714                                         totalRecords : 0
16715                                 };
16716                         }
16717             var record = new Record(values, id);
16718             record.json = n;
16719             records[i] = record;
16720         }
16721         return {
16722             raw : o,
16723             success : success,
16724             records : records,
16725             totalRecords : totalRecords
16726         };
16727     },
16728     // used when loading children.. @see loadDataFromChildren
16729     toLoadData: function(rec)
16730     {
16731         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16732         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16733         return { data : data, total : data.length };
16734         
16735     }
16736 });/*
16737  * Based on:
16738  * Ext JS Library 1.1.1
16739  * Copyright(c) 2006-2007, Ext JS, LLC.
16740  *
16741  * Originally Released Under LGPL - original licence link has changed is not relivant.
16742  *
16743  * Fork - LGPL
16744  * <script type="text/javascript">
16745  */
16746
16747 /**
16748  * @class Roo.data.ArrayReader
16749  * @extends Roo.data.DataReader
16750  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16751  * Each element of that Array represents a row of data fields. The
16752  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16753  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16754  * <p>
16755  * Example code:.
16756  * <pre><code>
16757 var RecordDef = Roo.data.Record.create([
16758     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16759     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16760 ]);
16761 var myReader = new Roo.data.ArrayReader({
16762     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16763 }, RecordDef);
16764 </code></pre>
16765  * <p>
16766  * This would consume an Array like this:
16767  * <pre><code>
16768 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16769   </code></pre>
16770  
16771  * @constructor
16772  * Create a new JsonReader
16773  * @param {Object} meta Metadata configuration options.
16774  * @param {Object|Array} recordType Either an Array of field definition objects
16775  * 
16776  * @cfg {Array} fields Array of field definition objects
16777  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16778  * as specified to {@link Roo.data.Record#create},
16779  * or an {@link Roo.data.Record} object
16780  *
16781  * 
16782  * created using {@link Roo.data.Record#create}.
16783  */
16784 Roo.data.ArrayReader = function(meta, recordType)
16785 {    
16786     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16787 };
16788
16789 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16790     
16791       /**
16792      * Create a data block containing Roo.data.Records from an XML document.
16793      * @param {Object} o An Array of row objects which represents the dataset.
16794      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16795      * a cache of Roo.data.Records.
16796      */
16797     readRecords : function(o)
16798     {
16799         var sid = this.meta ? this.meta.id : null;
16800         var recordType = this.recordType, fields = recordType.prototype.fields;
16801         var records = [];
16802         var root = o;
16803         for(var i = 0; i < root.length; i++){
16804             var n = root[i];
16805             var values = {};
16806             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16807             for(var j = 0, jlen = fields.length; j < jlen; j++){
16808                 var f = fields.items[j];
16809                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16810                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16811                 v = f.convert(v);
16812                 values[f.name] = v;
16813             }
16814             var record = new recordType(values, id);
16815             record.json = n;
16816             records[records.length] = record;
16817         }
16818         return {
16819             records : records,
16820             totalRecords : records.length
16821         };
16822     },
16823     // used when loading children.. @see loadDataFromChildren
16824     toLoadData: function(rec)
16825     {
16826         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16827         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16828         
16829     }
16830     
16831     
16832 });/*
16833  * - LGPL
16834  * * 
16835  */
16836
16837 /**
16838  * @class Roo.bootstrap.form.ComboBox
16839  * @extends Roo.bootstrap.form.TriggerField
16840  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16841  * @cfg {Boolean} append (true|false) default false
16842  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16843  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16844  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16845  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16846  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16847  * @cfg {Boolean} animate default true
16848  * @cfg {Boolean} emptyResultText only for touch device
16849  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16850  * @cfg {String} emptyTitle default ''
16851  * @cfg {Number} width fixed with? experimental
16852  * @constructor
16853  * Create a new ComboBox.
16854  * @param {Object} config Configuration options
16855  */
16856 Roo.bootstrap.form.ComboBox = function(config){
16857     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16858     this.addEvents({
16859         /**
16860          * @event expand
16861          * Fires when the dropdown list is expanded
16862         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16863         */
16864         'expand' : true,
16865         /**
16866          * @event collapse
16867          * Fires when the dropdown list is collapsed
16868         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16869         */
16870         'collapse' : true,
16871         /**
16872          * @event beforeselect
16873          * Fires before a list item is selected. Return false to cancel the selection.
16874         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16875         * @param {Roo.data.Record} record The data record returned from the underlying store
16876         * @param {Number} index The index of the selected item in the dropdown list
16877         */
16878         'beforeselect' : true,
16879         /**
16880          * @event select
16881          * Fires when a list item is selected
16882         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16883         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16884         * @param {Number} index The index of the selected item in the dropdown list
16885         */
16886         'select' : true,
16887         /**
16888          * @event beforequery
16889          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16890          * The event object passed has these properties:
16891         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16892         * @param {String} query The query
16893         * @param {Boolean} forceAll true to force "all" query
16894         * @param {Boolean} cancel true to cancel the query
16895         * @param {Object} e The query event object
16896         */
16897         'beforequery': true,
16898          /**
16899          * @event add
16900          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16901         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902         */
16903         'add' : true,
16904         /**
16905          * @event edit
16906          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16907         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16908         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16909         */
16910         'edit' : true,
16911         /**
16912          * @event remove
16913          * Fires when the remove value from the combobox array
16914         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16915         */
16916         'remove' : true,
16917         /**
16918          * @event afterremove
16919          * Fires when the remove value from the combobox array
16920         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16921         */
16922         'afterremove' : true,
16923         /**
16924          * @event specialfilter
16925          * Fires when specialfilter
16926             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16927             */
16928         'specialfilter' : true,
16929         /**
16930          * @event tick
16931          * Fires when tick the element
16932             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16933             */
16934         'tick' : true,
16935         /**
16936          * @event touchviewdisplay
16937          * Fires when touch view require special display (default is using displayField)
16938             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16939             * @param {Object} cfg set html .
16940             */
16941         'touchviewdisplay' : true
16942         
16943     });
16944     
16945     this.item = [];
16946     this.tickItems = [];
16947     
16948     this.selectedIndex = -1;
16949     if(this.mode == 'local'){
16950         if(config.queryDelay === undefined){
16951             this.queryDelay = 10;
16952         }
16953         if(config.minChars === undefined){
16954             this.minChars = 0;
16955         }
16956     }
16957 };
16958
16959 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16960      
16961     /**
16962      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16963      * rendering into an Roo.Editor, defaults to false)
16964      */
16965     /**
16966      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16967      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16968      */
16969     /**
16970      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16971      */
16972     /**
16973      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16974      * the dropdown list (defaults to undefined, with no header element)
16975      */
16976
16977      /**
16978      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16979      */
16980      
16981      /**
16982      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16983      */
16984     listWidth: undefined,
16985     /**
16986      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16987      * mode = 'remote' or 'text' if mode = 'local')
16988      */
16989     displayField: undefined,
16990     
16991     /**
16992      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16993      * mode = 'remote' or 'value' if mode = 'local'). 
16994      * Note: use of a valueField requires the user make a selection
16995      * in order for a value to be mapped.
16996      */
16997     valueField: undefined,
16998     /**
16999      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17000      */
17001     modalTitle : '',
17002     
17003     /**
17004      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17005      * field's data value (defaults to the underlying DOM element's name)
17006      */
17007     hiddenName: undefined,
17008     /**
17009      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17010      */
17011     listClass: '',
17012     /**
17013      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17014      */
17015     selectedClass: 'active',
17016     
17017     /**
17018      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17019      */
17020     shadow:'sides',
17021     /**
17022      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17023      * anchor positions (defaults to 'tl-bl')
17024      */
17025     listAlign: 'tl-bl?',
17026     /**
17027      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17028      */
17029     maxHeight: 300,
17030     /**
17031      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17032      * query specified by the allQuery config option (defaults to 'query')
17033      */
17034     triggerAction: 'query',
17035     /**
17036      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17037      * (defaults to 4, does not apply if editable = false)
17038      */
17039     minChars : 4,
17040     /**
17041      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17042      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17043      */
17044     typeAhead: false,
17045     /**
17046      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17047      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17048      */
17049     queryDelay: 500,
17050     /**
17051      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17052      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17053      */
17054     pageSize: 0,
17055     /**
17056      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17057      * when editable = true (defaults to false)
17058      */
17059     selectOnFocus:false,
17060     /**
17061      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17062      */
17063     queryParam: 'query',
17064     /**
17065      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17066      * when mode = 'remote' (defaults to 'Loading...')
17067      */
17068     loadingText: 'Loading...',
17069     /**
17070      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17071      */
17072     resizable: false,
17073     /**
17074      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17075      */
17076     handleHeight : 8,
17077     /**
17078      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17079      * traditional select (defaults to true)
17080      */
17081     editable: true,
17082     /**
17083      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17084      */
17085     allQuery: '',
17086     /**
17087      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17088      */
17089     mode: 'remote',
17090     /**
17091      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17092      * listWidth has a higher value)
17093      */
17094     minListWidth : 70,
17095     /**
17096      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17097      * allow the user to set arbitrary text into the field (defaults to false)
17098      */
17099     forceSelection:false,
17100     /**
17101      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17102      * if typeAhead = true (defaults to 250)
17103      */
17104     typeAheadDelay : 250,
17105     /**
17106      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17107      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17108      */
17109     valueNotFoundText : undefined,
17110     /**
17111      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17112      */
17113     blockFocus : false,
17114     
17115     /**
17116      * @cfg {Boolean} disableClear Disable showing of clear button.
17117      */
17118     disableClear : false,
17119     /**
17120      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17121      */
17122     alwaysQuery : false,
17123     
17124     /**
17125      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17126      */
17127     multiple : false,
17128     
17129     /**
17130      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17131      */
17132     invalidClass : "has-warning",
17133     
17134     /**
17135      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17136      */
17137     validClass : "has-success",
17138     
17139     /**
17140      * @cfg {Boolean} specialFilter (true|false) special filter default false
17141      */
17142     specialFilter : false,
17143     
17144     /**
17145      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17146      */
17147     mobileTouchView : true,
17148     
17149     /**
17150      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17151      */
17152     useNativeIOS : false,
17153     
17154     /**
17155      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17156      */
17157     mobile_restrict_height : false,
17158     
17159     ios_options : false,
17160     
17161     //private
17162     addicon : false,
17163     editicon: false,
17164     
17165     page: 0,
17166     hasQuery: false,
17167     append: false,
17168     loadNext: false,
17169     autoFocus : true,
17170     tickable : false,
17171     btnPosition : 'right',
17172     triggerList : true,
17173     showToggleBtn : true,
17174     animate : true,
17175     emptyResultText: 'Empty',
17176     triggerText : 'Select',
17177     emptyTitle : '',
17178     width : false,
17179     
17180     // element that contains real text value.. (when hidden is used..)
17181     
17182     getAutoCreate : function()
17183     {   
17184         var cfg = false;
17185         //render
17186         /*
17187          * Render classic select for iso
17188          */
17189         
17190         if(Roo.isIOS && this.useNativeIOS){
17191             cfg = this.getAutoCreateNativeIOS();
17192             return cfg;
17193         }
17194         
17195         /*
17196          * Touch Devices
17197          */
17198         
17199         if(Roo.isTouch && this.mobileTouchView){
17200             cfg = this.getAutoCreateTouchView();
17201             return cfg;;
17202         }
17203         
17204         /*
17205          *  Normal ComboBox
17206          */
17207         if(!this.tickable){
17208             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17209             return cfg;
17210         }
17211         
17212         /*
17213          *  ComboBox with tickable selections
17214          */
17215              
17216         var align = this.labelAlign || this.parentLabelAlign();
17217         
17218         cfg = {
17219             cls : 'form-group roo-combobox-tickable' //input-group
17220         };
17221         
17222         var btn_text_select = '';
17223         var btn_text_done = '';
17224         var btn_text_cancel = '';
17225         
17226         if (this.btn_text_show) {
17227             btn_text_select = 'Select';
17228             btn_text_done = 'Done';
17229             btn_text_cancel = 'Cancel'; 
17230         }
17231         
17232         var buttons = {
17233             tag : 'div',
17234             cls : 'tickable-buttons',
17235             cn : [
17236                 {
17237                     tag : 'button',
17238                     type : 'button',
17239                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17240                     //html : this.triggerText
17241                     html: btn_text_select
17242                 },
17243                 {
17244                     tag : 'button',
17245                     type : 'button',
17246                     name : 'ok',
17247                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17248                     //html : 'Done'
17249                     html: btn_text_done
17250                 },
17251                 {
17252                     tag : 'button',
17253                     type : 'button',
17254                     name : 'cancel',
17255                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17256                     //html : 'Cancel'
17257                     html: btn_text_cancel
17258                 }
17259             ]
17260         };
17261         
17262         if(this.editable){
17263             buttons.cn.unshift({
17264                 tag: 'input',
17265                 cls: 'roo-select2-search-field-input'
17266             });
17267         }
17268         
17269         var _this = this;
17270         
17271         Roo.each(buttons.cn, function(c){
17272             if (_this.size) {
17273                 c.cls += ' btn-' + _this.size;
17274             }
17275
17276             if (_this.disabled) {
17277                 c.disabled = true;
17278             }
17279         });
17280         
17281         var box = {
17282             tag: 'div',
17283             style : 'display: contents',
17284             cn: [
17285                 {
17286                     tag: 'input',
17287                     type : 'hidden',
17288                     cls: 'form-hidden-field'
17289                 },
17290                 {
17291                     tag: 'ul',
17292                     cls: 'roo-select2-choices',
17293                     cn:[
17294                         {
17295                             tag: 'li',
17296                             cls: 'roo-select2-search-field',
17297                             cn: [
17298                                 buttons
17299                             ]
17300                         }
17301                     ]
17302                 }
17303             ]
17304         };
17305         
17306         var combobox = {
17307             cls: 'roo-select2-container input-group roo-select2-container-multi',
17308             cn: [
17309                 
17310                 box
17311 //                {
17312 //                    tag: 'ul',
17313 //                    cls: 'typeahead typeahead-long dropdown-menu',
17314 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17315 //                }
17316             ]
17317         };
17318         
17319         if(this.hasFeedback && !this.allowBlank){
17320             
17321             var feedback = {
17322                 tag: 'span',
17323                 cls: 'glyphicon form-control-feedback'
17324             };
17325
17326             combobox.cn.push(feedback);
17327         }
17328         
17329         
17330         
17331         var indicator = {
17332             tag : 'i',
17333             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17334             tooltip : 'This field is required'
17335         };
17336         if (Roo.bootstrap.version == 4) {
17337             indicator = {
17338                 tag : 'i',
17339                 style : 'display:none'
17340             };
17341         }
17342         if (align ==='left' && this.fieldLabel.length) {
17343             
17344             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17345             
17346             cfg.cn = [
17347                 indicator,
17348                 {
17349                     tag: 'label',
17350                     'for' :  id,
17351                     cls : 'control-label col-form-label',
17352                     html : this.fieldLabel
17353
17354                 },
17355                 {
17356                     cls : "", 
17357                     cn: [
17358                         combobox
17359                     ]
17360                 }
17361
17362             ];
17363             
17364             var labelCfg = cfg.cn[1];
17365             var contentCfg = cfg.cn[2];
17366             
17367
17368             if(this.indicatorpos == 'right'){
17369                 
17370                 cfg.cn = [
17371                     {
17372                         tag: 'label',
17373                         'for' :  id,
17374                         cls : 'control-label col-form-label',
17375                         cn : [
17376                             {
17377                                 tag : 'span',
17378                                 html : this.fieldLabel
17379                             },
17380                             indicator
17381                         ]
17382                     },
17383                     {
17384                         cls : "",
17385                         cn: [
17386                             combobox
17387                         ]
17388                     }
17389
17390                 ];
17391                 
17392                 
17393                 
17394                 labelCfg = cfg.cn[0];
17395                 contentCfg = cfg.cn[1];
17396             
17397             }
17398             
17399             if(this.labelWidth > 12){
17400                 labelCfg.style = "width: " + this.labelWidth + 'px';
17401             }
17402             if(this.width * 1 > 0){
17403                 contentCfg.style = "width: " + this.width + 'px';
17404             }
17405             if(this.labelWidth < 13 && this.labelmd == 0){
17406                 this.labelmd = this.labelWidth;
17407             }
17408             
17409             if(this.labellg > 0){
17410                 labelCfg.cls += ' col-lg-' + this.labellg;
17411                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17412             }
17413             
17414             if(this.labelmd > 0){
17415                 labelCfg.cls += ' col-md-' + this.labelmd;
17416                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17417             }
17418             
17419             if(this.labelsm > 0){
17420                 labelCfg.cls += ' col-sm-' + this.labelsm;
17421                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17422             }
17423             
17424             if(this.labelxs > 0){
17425                 labelCfg.cls += ' col-xs-' + this.labelxs;
17426                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17427             }
17428                 
17429                 
17430         } else if ( this.fieldLabel.length) {
17431 //                Roo.log(" label");
17432                  cfg.cn = [
17433                    indicator,
17434                     {
17435                         tag: 'label',
17436                         //cls : 'input-group-addon',
17437                         html : this.fieldLabel
17438                     },
17439                     combobox
17440                 ];
17441                 
17442                 if(this.indicatorpos == 'right'){
17443                     cfg.cn = [
17444                         {
17445                             tag: 'label',
17446                             //cls : 'input-group-addon',
17447                             html : this.fieldLabel
17448                         },
17449                         indicator,
17450                         combobox
17451                     ];
17452                     
17453                 }
17454
17455         } else {
17456             
17457 //                Roo.log(" no label && no align");
17458                 cfg = combobox
17459                      
17460                 
17461         }
17462          
17463         var settings=this;
17464         ['xs','sm','md','lg'].map(function(size){
17465             if (settings[size]) {
17466                 cfg.cls += ' col-' + size + '-' + settings[size];
17467             }
17468         });
17469         
17470         return cfg;
17471         
17472     },
17473     
17474     _initEventsCalled : false,
17475     
17476     // private
17477     initEvents: function()
17478     {   
17479         if (this._initEventsCalled) { // as we call render... prevent looping...
17480             return;
17481         }
17482         this._initEventsCalled = true;
17483         
17484         if (!this.store) {
17485             throw "can not find store for combo";
17486         }
17487         
17488         this.indicator = this.indicatorEl();
17489         
17490         this.store = Roo.factory(this.store, Roo.data);
17491         this.store.parent = this;
17492         
17493         // if we are building from html. then this element is so complex, that we can not really
17494         // use the rendered HTML.
17495         // so we have to trash and replace the previous code.
17496         if (Roo.XComponent.build_from_html) {
17497             // remove this element....
17498             var e = this.el.dom, k=0;
17499             while (e ) { e = e.previousSibling;  ++k;}
17500
17501             this.el.remove();
17502             
17503             this.el=false;
17504             this.rendered = false;
17505             
17506             this.render(this.parent().getChildContainer(true), k);
17507         }
17508         
17509         if(Roo.isIOS && this.useNativeIOS){
17510             this.initIOSView();
17511             return;
17512         }
17513         
17514         /*
17515          * Touch Devices
17516          */
17517         
17518         if(Roo.isTouch && this.mobileTouchView){
17519             this.initTouchView();
17520             return;
17521         }
17522         
17523         if(this.tickable){
17524             this.initTickableEvents();
17525             return;
17526         }
17527         
17528         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17529         
17530         if(this.hiddenName){
17531             
17532             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17533             
17534             this.hiddenField.dom.value =
17535                 this.hiddenValue !== undefined ? this.hiddenValue :
17536                 this.value !== undefined ? this.value : '';
17537
17538             // prevent input submission
17539             this.el.dom.removeAttribute('name');
17540             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17541              
17542              
17543         }
17544         //if(Roo.isGecko){
17545         //    this.el.dom.setAttribute('autocomplete', 'off');
17546         //}
17547         
17548         var cls = 'x-combo-list';
17549         
17550         //this.list = new Roo.Layer({
17551         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17552         //});
17553         
17554         var _this = this;
17555         
17556         (function(){
17557             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17558             _this.list.setWidth(lw);
17559         }).defer(100);
17560         
17561         this.list.on('mouseover', this.onViewOver, this);
17562         this.list.on('mousemove', this.onViewMove, this);
17563         this.list.on('scroll', this.onViewScroll, this);
17564         
17565         /*
17566         this.list.swallowEvent('mousewheel');
17567         this.assetHeight = 0;
17568
17569         if(this.title){
17570             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17571             this.assetHeight += this.header.getHeight();
17572         }
17573
17574         this.innerList = this.list.createChild({cls:cls+'-inner'});
17575         this.innerList.on('mouseover', this.onViewOver, this);
17576         this.innerList.on('mousemove', this.onViewMove, this);
17577         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17578         
17579         if(this.allowBlank && !this.pageSize && !this.disableClear){
17580             this.footer = this.list.createChild({cls:cls+'-ft'});
17581             this.pageTb = new Roo.Toolbar(this.footer);
17582            
17583         }
17584         if(this.pageSize){
17585             this.footer = this.list.createChild({cls:cls+'-ft'});
17586             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17587                     {pageSize: this.pageSize});
17588             
17589         }
17590         
17591         if (this.pageTb && this.allowBlank && !this.disableClear) {
17592             var _this = this;
17593             this.pageTb.add(new Roo.Toolbar.Fill(), {
17594                 cls: 'x-btn-icon x-btn-clear',
17595                 text: '&#160;',
17596                 handler: function()
17597                 {
17598                     _this.collapse();
17599                     _this.clearValue();
17600                     _this.onSelect(false, -1);
17601                 }
17602             });
17603         }
17604         if (this.footer) {
17605             this.assetHeight += this.footer.getHeight();
17606         }
17607         */
17608             
17609         if(!this.tpl){
17610             this.tpl = Roo.bootstrap.version == 4 ?
17611                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17612                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17613         }
17614
17615         this.view = new Roo.View(this.list, this.tpl, {
17616             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17617         });
17618         //this.view.wrapEl.setDisplayed(false);
17619         this.view.on('click', this.onViewClick, this);
17620         
17621         
17622         this.store.on('beforeload', this.onBeforeLoad, this);
17623         this.store.on('load', this.onLoad, this);
17624         this.store.on('loadexception', this.onLoadException, this);
17625         /*
17626         if(this.resizable){
17627             this.resizer = new Roo.Resizable(this.list,  {
17628                pinned:true, handles:'se'
17629             });
17630             this.resizer.on('resize', function(r, w, h){
17631                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17632                 this.listWidth = w;
17633                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17634                 this.restrictHeight();
17635             }, this);
17636             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17637         }
17638         */
17639         if(!this.editable){
17640             this.editable = true;
17641             this.setEditable(false);
17642         }
17643         
17644         /*
17645         
17646         if (typeof(this.events.add.listeners) != 'undefined') {
17647             
17648             this.addicon = this.wrap.createChild(
17649                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17650        
17651             this.addicon.on('click', function(e) {
17652                 this.fireEvent('add', this);
17653             }, this);
17654         }
17655         if (typeof(this.events.edit.listeners) != 'undefined') {
17656             
17657             this.editicon = this.wrap.createChild(
17658                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17659             if (this.addicon) {
17660                 this.editicon.setStyle('margin-left', '40px');
17661             }
17662             this.editicon.on('click', function(e) {
17663                 
17664                 // we fire even  if inothing is selected..
17665                 this.fireEvent('edit', this, this.lastData );
17666                 
17667             }, this);
17668         }
17669         */
17670         
17671         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17672             "up" : function(e){
17673                 this.inKeyMode = true;
17674                 this.selectPrev();
17675             },
17676
17677             "down" : function(e){
17678                 if(!this.isExpanded()){
17679                     this.onTriggerClick();
17680                 }else{
17681                     this.inKeyMode = true;
17682                     this.selectNext();
17683                 }
17684             },
17685
17686             "enter" : function(e){
17687 //                this.onViewClick();
17688                 //return true;
17689                 this.collapse();
17690                 
17691                 if(this.fireEvent("specialkey", this, e)){
17692                     this.onViewClick(false);
17693                 }
17694                 
17695                 return true;
17696             },
17697
17698             "esc" : function(e){
17699                 this.collapse();
17700             },
17701
17702             "tab" : function(e){
17703                 this.collapse();
17704                 
17705                 if(this.fireEvent("specialkey", this, e)){
17706                     this.onViewClick(false);
17707                 }
17708                 
17709                 return true;
17710             },
17711
17712             scope : this,
17713
17714             doRelay : function(foo, bar, hname){
17715                 if(hname == 'down' || this.scope.isExpanded()){
17716                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17717                 }
17718                 return true;
17719             },
17720
17721             forceKeyDown: true
17722         });
17723         
17724         
17725         this.queryDelay = Math.max(this.queryDelay || 10,
17726                 this.mode == 'local' ? 10 : 250);
17727         
17728         
17729         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17730         
17731         if(this.typeAhead){
17732             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17733         }
17734         if(this.editable !== false){
17735             this.inputEl().on("keyup", this.onKeyUp, this);
17736         }
17737         if(this.forceSelection){
17738             this.inputEl().on('blur', this.doForce, this);
17739         }
17740         
17741         if(this.multiple){
17742             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17743             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17744         }
17745     },
17746     
17747     initTickableEvents: function()
17748     {   
17749         this.createList();
17750         
17751         if(this.hiddenName){
17752             
17753             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17754             
17755             this.hiddenField.dom.value =
17756                 this.hiddenValue !== undefined ? this.hiddenValue :
17757                 this.value !== undefined ? this.value : '';
17758
17759             // prevent input submission
17760             this.el.dom.removeAttribute('name');
17761             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17762              
17763              
17764         }
17765         
17766 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17767         
17768         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17769         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17770         if(this.triggerList){
17771             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17772         }
17773          
17774         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17775         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17776         
17777         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17778         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17779         
17780         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17781         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17782         
17783         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17784         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17785         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17786         
17787         this.okBtn.hide();
17788         this.cancelBtn.hide();
17789         
17790         var _this = this;
17791         
17792         (function(){
17793             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17794             _this.list.setWidth(lw);
17795         }).defer(100);
17796         
17797         this.list.on('mouseover', this.onViewOver, this);
17798         this.list.on('mousemove', this.onViewMove, this);
17799         
17800         this.list.on('scroll', this.onViewScroll, this);
17801         
17802         if(!this.tpl){
17803             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17804                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17805         }
17806
17807         this.view = new Roo.View(this.list, this.tpl, {
17808             singleSelect:true,
17809             tickable:true,
17810             parent:this,
17811             store: this.store,
17812             selectedClass: this.selectedClass
17813         });
17814         
17815         //this.view.wrapEl.setDisplayed(false);
17816         this.view.on('click', this.onViewClick, this);
17817         
17818         
17819         
17820         this.store.on('beforeload', this.onBeforeLoad, this);
17821         this.store.on('load', this.onLoad, this);
17822         this.store.on('loadexception', this.onLoadException, this);
17823         
17824         if(this.editable){
17825             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17826                 "up" : function(e){
17827                     this.inKeyMode = true;
17828                     this.selectPrev();
17829                 },
17830
17831                 "down" : function(e){
17832                     this.inKeyMode = true;
17833                     this.selectNext();
17834                 },
17835
17836                 "enter" : function(e){
17837                     if(this.fireEvent("specialkey", this, e)){
17838                         this.onViewClick(false);
17839                     }
17840                     
17841                     return true;
17842                 },
17843
17844                 "esc" : function(e){
17845                     this.onTickableFooterButtonClick(e, false, false);
17846                 },
17847
17848                 "tab" : function(e){
17849                     this.fireEvent("specialkey", this, e);
17850                     
17851                     this.onTickableFooterButtonClick(e, false, false);
17852                     
17853                     return true;
17854                 },
17855
17856                 scope : this,
17857
17858                 doRelay : function(e, fn, key){
17859                     if(this.scope.isExpanded()){
17860                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17861                     }
17862                     return true;
17863                 },
17864
17865                 forceKeyDown: true
17866             });
17867         }
17868         
17869         this.queryDelay = Math.max(this.queryDelay || 10,
17870                 this.mode == 'local' ? 10 : 250);
17871         
17872         
17873         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17874         
17875         if(this.typeAhead){
17876             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17877         }
17878         
17879         if(this.editable !== false){
17880             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17881         }
17882         
17883         this.indicator = this.indicatorEl();
17884         
17885         if(this.indicator){
17886             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17887             this.indicator.hide();
17888         }
17889         
17890     },
17891
17892     onDestroy : function(){
17893         if(this.view){
17894             this.view.setStore(null);
17895             this.view.el.removeAllListeners();
17896             this.view.el.remove();
17897             this.view.purgeListeners();
17898         }
17899         if(this.list){
17900             this.list.dom.innerHTML  = '';
17901         }
17902         
17903         if(this.store){
17904             this.store.un('beforeload', this.onBeforeLoad, this);
17905             this.store.un('load', this.onLoad, this);
17906             this.store.un('loadexception', this.onLoadException, this);
17907         }
17908         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17909     },
17910
17911     // private
17912     fireKey : function(e){
17913         if(e.isNavKeyPress() && !this.list.isVisible()){
17914             this.fireEvent("specialkey", this, e);
17915         }
17916     },
17917
17918     // private
17919     onResize: function(w, h)
17920     {
17921         
17922         
17923 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17924 //        
17925 //        if(typeof w != 'number'){
17926 //            // we do not handle it!?!?
17927 //            return;
17928 //        }
17929 //        var tw = this.trigger.getWidth();
17930 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17931 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17932 //        var x = w - tw;
17933 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17934 //            
17935 //        //this.trigger.setStyle('left', x+'px');
17936 //        
17937 //        if(this.list && this.listWidth === undefined){
17938 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17939 //            this.list.setWidth(lw);
17940 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17941 //        }
17942         
17943     
17944         
17945     },
17946
17947     /**
17948      * Allow or prevent the user from directly editing the field text.  If false is passed,
17949      * the user will only be able to select from the items defined in the dropdown list.  This method
17950      * is the runtime equivalent of setting the 'editable' config option at config time.
17951      * @param {Boolean} value True to allow the user to directly edit the field text
17952      */
17953     setEditable : function(value){
17954         if(value == this.editable){
17955             return;
17956         }
17957         this.editable = value;
17958         if(!value){
17959             this.inputEl().dom.setAttribute('readOnly', true);
17960             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17961             this.inputEl().addClass('x-combo-noedit');
17962         }else{
17963             this.inputEl().dom.removeAttribute('readOnly');
17964             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17965             this.inputEl().removeClass('x-combo-noedit');
17966         }
17967     },
17968
17969     // private
17970     
17971     onBeforeLoad : function(combo,opts){
17972         if(!this.hasFocus){
17973             return;
17974         }
17975          if (!opts.add) {
17976             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17977          }
17978         this.restrictHeight();
17979         this.selectedIndex = -1;
17980     },
17981
17982     // private
17983     onLoad : function(){
17984         
17985         this.hasQuery = false;
17986         
17987         if(!this.hasFocus){
17988             return;
17989         }
17990         
17991         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17992             this.loading.hide();
17993         }
17994         
17995         if(this.store.getCount() > 0){
17996             
17997             this.expand();
17998             this.restrictHeight();
17999             if(this.lastQuery == this.allQuery){
18000                 if(this.editable && !this.tickable){
18001                     this.inputEl().dom.select();
18002                 }
18003                 
18004                 if(
18005                     !this.selectByValue(this.value, true) &&
18006                     this.autoFocus && 
18007                     (
18008                         !this.store.lastOptions ||
18009                         typeof(this.store.lastOptions.add) == 'undefined' || 
18010                         this.store.lastOptions.add != true
18011                     )
18012                 ){
18013                     this.select(0, true);
18014                 }
18015             }else{
18016                 if(this.autoFocus){
18017                     this.selectNext();
18018                 }
18019                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18020                     this.taTask.delay(this.typeAheadDelay);
18021                 }
18022             }
18023         }else{
18024             this.onEmptyResults();
18025         }
18026         
18027         //this.el.focus();
18028     },
18029     // private
18030     onLoadException : function()
18031     {
18032         this.hasQuery = false;
18033         
18034         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18035             this.loading.hide();
18036         }
18037         
18038         if(this.tickable && this.editable){
18039             return;
18040         }
18041         
18042         this.collapse();
18043         // only causes errors at present
18044         //Roo.log(this.store.reader.jsonData);
18045         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18046             // fixme
18047             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18048         //}
18049         
18050         
18051     },
18052     // private
18053     onTypeAhead : function(){
18054         if(this.store.getCount() > 0){
18055             var r = this.store.getAt(0);
18056             var newValue = r.data[this.displayField];
18057             var len = newValue.length;
18058             var selStart = this.getRawValue().length;
18059             
18060             if(selStart != len){
18061                 this.setRawValue(newValue);
18062                 this.selectText(selStart, newValue.length);
18063             }
18064         }
18065     },
18066
18067     // private
18068     onSelect : function(record, index){
18069         
18070         if(this.fireEvent('beforeselect', this, record, index) !== false){
18071         
18072             this.setFromData(index > -1 ? record.data : false);
18073             
18074             this.collapse();
18075             this.fireEvent('select', this, record, index);
18076         }
18077     },
18078
18079     /**
18080      * Returns the currently selected field value or empty string if no value is set.
18081      * @return {String} value The selected value
18082      */
18083     getValue : function()
18084     {
18085         if(Roo.isIOS && this.useNativeIOS){
18086             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18087         }
18088         
18089         if(this.multiple){
18090             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18091         }
18092         
18093         if(this.valueField){
18094             return typeof this.value != 'undefined' ? this.value : '';
18095         }else{
18096             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18097         }
18098     },
18099     
18100     getRawValue : function()
18101     {
18102         if(Roo.isIOS && this.useNativeIOS){
18103             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18104         }
18105         
18106         var v = this.inputEl().getValue();
18107         
18108         return v;
18109     },
18110
18111     /**
18112      * Clears any text/value currently set in the field
18113      */
18114     clearValue : function(){
18115         
18116         if(this.hiddenField){
18117             this.hiddenField.dom.value = '';
18118         }
18119         this.value = '';
18120         this.setRawValue('');
18121         this.lastSelectionText = '';
18122         this.lastData = false;
18123         
18124         var close = this.closeTriggerEl();
18125         
18126         if(close){
18127             close.hide();
18128         }
18129         
18130         this.validate();
18131         
18132     },
18133
18134     /**
18135      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18136      * will be displayed in the field.  If the value does not match the data value of an existing item,
18137      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18138      * Otherwise the field will be blank (although the value will still be set).
18139      * @param {String} value The value to match
18140      */
18141     setValue : function(v)
18142     {
18143         if(Roo.isIOS && this.useNativeIOS){
18144             this.setIOSValue(v);
18145             return;
18146         }
18147         
18148         if(this.multiple){
18149             this.syncValue();
18150             return;
18151         }
18152         
18153         var text = v;
18154         if(this.valueField){
18155             var r = this.findRecord(this.valueField, v);
18156             if(r){
18157                 text = r.data[this.displayField];
18158             }else if(this.valueNotFoundText !== undefined){
18159                 text = this.valueNotFoundText;
18160             }
18161         }
18162         this.lastSelectionText = text;
18163         if(this.hiddenField){
18164             this.hiddenField.dom.value = v;
18165         }
18166         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18167         this.value = v;
18168         
18169         var close = this.closeTriggerEl();
18170         
18171         if(close){
18172             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18173         }
18174         
18175         this.validate();
18176     },
18177     /**
18178      * @property {Object} the last set data for the element
18179      */
18180     
18181     lastData : false,
18182     /**
18183      * Sets the value of the field based on a object which is related to the record format for the store.
18184      * @param {Object} value the value to set as. or false on reset?
18185      */
18186     setFromData : function(o){
18187         
18188         if(this.multiple){
18189             this.addItem(o);
18190             return;
18191         }
18192             
18193         var dv = ''; // display value
18194         var vv = ''; // value value..
18195         this.lastData = o;
18196         if (this.displayField) {
18197             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18198         } else {
18199             // this is an error condition!!!
18200             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18201         }
18202         
18203         if(this.valueField){
18204             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18205         }
18206         
18207         var close = this.closeTriggerEl();
18208         
18209         if(close){
18210             if(dv.length || vv * 1 > 0){
18211                 close.show() ;
18212                 this.blockFocus=true;
18213             } else {
18214                 close.hide();
18215             }             
18216         }
18217         
18218         if(this.hiddenField){
18219             this.hiddenField.dom.value = vv;
18220             
18221             this.lastSelectionText = dv;
18222             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18223             this.value = vv;
18224             return;
18225         }
18226         // no hidden field.. - we store the value in 'value', but still display
18227         // display field!!!!
18228         this.lastSelectionText = dv;
18229         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18230         this.value = vv;
18231         
18232         
18233         
18234     },
18235     // private
18236     reset : function(){
18237         // overridden so that last data is reset..
18238         
18239         if(this.multiple){
18240             this.clearItem();
18241             return;
18242         }
18243         
18244         this.setValue(this.originalValue);
18245         //this.clearInvalid();
18246         this.lastData = false;
18247         if (this.view) {
18248             this.view.clearSelections();
18249         }
18250         
18251         this.validate();
18252     },
18253     // private
18254     findRecord : function(prop, value){
18255         var record;
18256         if(this.store.getCount() > 0){
18257             this.store.each(function(r){
18258                 if(r.data[prop] == value){
18259                     record = r;
18260                     return false;
18261                 }
18262                 return true;
18263             });
18264         }
18265         return record;
18266     },
18267     
18268     getName: function()
18269     {
18270         // returns hidden if it's set..
18271         if (!this.rendered) {return ''};
18272         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18273         
18274     },
18275     // private
18276     onViewMove : function(e, t){
18277         this.inKeyMode = false;
18278     },
18279
18280     // private
18281     onViewOver : function(e, t){
18282         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18283             return;
18284         }
18285         var item = this.view.findItemFromChild(t);
18286         
18287         if(item){
18288             var index = this.view.indexOf(item);
18289             this.select(index, false);
18290         }
18291     },
18292
18293     // private
18294     onViewClick : function(view, doFocus, el, e)
18295     {
18296         var index = this.view.getSelectedIndexes()[0];
18297         
18298         var r = this.store.getAt(index);
18299         
18300         if(this.tickable){
18301             
18302             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18303                 return;
18304             }
18305             
18306             var rm = false;
18307             var _this = this;
18308             
18309             Roo.each(this.tickItems, function(v,k){
18310                 
18311                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18312                     Roo.log(v);
18313                     _this.tickItems.splice(k, 1);
18314                     
18315                     if(typeof(e) == 'undefined' && view == false){
18316                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18317                     }
18318                     
18319                     rm = true;
18320                     return;
18321                 }
18322             });
18323             
18324             if(rm){
18325                 return;
18326             }
18327             
18328             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18329                 this.tickItems.push(r.data);
18330             }
18331             
18332             if(typeof(e) == 'undefined' && view == false){
18333                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18334             }
18335                     
18336             return;
18337         }
18338         
18339         if(r){
18340             this.onSelect(r, index);
18341         }
18342         if(doFocus !== false && !this.blockFocus){
18343             this.inputEl().focus();
18344         }
18345     },
18346
18347     // private
18348     restrictHeight : function(){
18349         //this.innerList.dom.style.height = '';
18350         //var inner = this.innerList.dom;
18351         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18352         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18353         //this.list.beginUpdate();
18354         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18355         this.list.alignTo(this.inputEl(), this.listAlign);
18356         this.list.alignTo(this.inputEl(), this.listAlign);
18357         //this.list.endUpdate();
18358     },
18359
18360     // private
18361     onEmptyResults : function(){
18362         
18363         if(this.tickable && this.editable){
18364             this.hasFocus = false;
18365             this.restrictHeight();
18366             return;
18367         }
18368         
18369         this.collapse();
18370     },
18371
18372     /**
18373      * Returns true if the dropdown list is expanded, else false.
18374      */
18375     isExpanded : function(){
18376         return this.list.isVisible();
18377     },
18378
18379     /**
18380      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18381      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18382      * @param {String} value The data value of the item to select
18383      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18384      * selected item if it is not currently in view (defaults to true)
18385      * @return {Boolean} True if the value matched an item in the list, else false
18386      */
18387     selectByValue : function(v, scrollIntoView){
18388         if(v !== undefined && v !== null){
18389             var r = this.findRecord(this.valueField || this.displayField, v);
18390             if(r){
18391                 this.select(this.store.indexOf(r), scrollIntoView);
18392                 return true;
18393             }
18394         }
18395         return false;
18396     },
18397
18398     /**
18399      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18400      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18401      * @param {Number} index The zero-based index of the list item to select
18402      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18403      * selected item if it is not currently in view (defaults to true)
18404      */
18405     select : function(index, scrollIntoView){
18406         this.selectedIndex = index;
18407         this.view.select(index);
18408         if(scrollIntoView !== false){
18409             var el = this.view.getNode(index);
18410             /*
18411              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18412              */
18413             if(el){
18414                 this.list.scrollChildIntoView(el, false);
18415             }
18416         }
18417     },
18418
18419     // private
18420     selectNext : function(){
18421         var ct = this.store.getCount();
18422         if(ct > 0){
18423             if(this.selectedIndex == -1){
18424                 this.select(0);
18425             }else if(this.selectedIndex < ct-1){
18426                 this.select(this.selectedIndex+1);
18427             }
18428         }
18429     },
18430
18431     // private
18432     selectPrev : function(){
18433         var ct = this.store.getCount();
18434         if(ct > 0){
18435             if(this.selectedIndex == -1){
18436                 this.select(0);
18437             }else if(this.selectedIndex != 0){
18438                 this.select(this.selectedIndex-1);
18439             }
18440         }
18441     },
18442
18443     // private
18444     onKeyUp : function(e){
18445         if(this.editable !== false && !e.isSpecialKey()){
18446             this.lastKey = e.getKey();
18447             this.dqTask.delay(this.queryDelay);
18448         }
18449     },
18450
18451     // private
18452     validateBlur : function(){
18453         return !this.list || !this.list.isVisible();   
18454     },
18455
18456     // private
18457     initQuery : function(){
18458         
18459         var v = this.getRawValue();
18460         
18461         if(this.tickable && this.editable){
18462             v = this.tickableInputEl().getValue();
18463         }
18464         
18465         this.doQuery(v);
18466     },
18467
18468     // private
18469     doForce : function(){
18470         if(this.inputEl().dom.value.length > 0){
18471             this.inputEl().dom.value =
18472                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18473              
18474         }
18475     },
18476
18477     /**
18478      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18479      * query allowing the query action to be canceled if needed.
18480      * @param {String} query The SQL query to execute
18481      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18482      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18483      * saved in the current store (defaults to false)
18484      */
18485     doQuery : function(q, forceAll){
18486         
18487         if(q === undefined || q === null){
18488             q = '';
18489         }
18490         var qe = {
18491             query: q,
18492             forceAll: forceAll,
18493             combo: this,
18494             cancel:false
18495         };
18496         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18497             return false;
18498         }
18499         q = qe.query;
18500         
18501         forceAll = qe.forceAll;
18502         if(forceAll === true || (q.length >= this.minChars)){
18503             
18504             this.hasQuery = true;
18505             
18506             if(this.lastQuery != q || this.alwaysQuery){
18507                 this.lastQuery = q;
18508                 if(this.mode == 'local'){
18509                     this.selectedIndex = -1;
18510                     if(forceAll){
18511                         this.store.clearFilter();
18512                     }else{
18513                         
18514                         if(this.specialFilter){
18515                             this.fireEvent('specialfilter', this);
18516                             this.onLoad();
18517                             return;
18518                         }
18519                         
18520                         this.store.filter(this.displayField, q);
18521                     }
18522                     
18523                     this.store.fireEvent("datachanged", this.store);
18524                     
18525                     this.onLoad();
18526                     
18527                     
18528                 }else{
18529                     
18530                     this.store.baseParams[this.queryParam] = q;
18531                     
18532                     var options = {params : this.getParams(q)};
18533                     
18534                     if(this.loadNext){
18535                         options.add = true;
18536                         options.params.start = this.page * this.pageSize;
18537                     }
18538                     
18539                     this.store.load(options);
18540                     
18541                     /*
18542                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18543                      *  we should expand the list on onLoad
18544                      *  so command out it
18545                      */
18546 //                    this.expand();
18547                 }
18548             }else{
18549                 this.selectedIndex = -1;
18550                 this.onLoad();   
18551             }
18552         }
18553         
18554         this.loadNext = false;
18555     },
18556     
18557     // private
18558     getParams : function(q){
18559         var p = {};
18560         //p[this.queryParam] = q;
18561         
18562         if(this.pageSize){
18563             p.start = 0;
18564             p.limit = this.pageSize;
18565         }
18566         return p;
18567     },
18568
18569     /**
18570      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18571      */
18572     collapse : function(){
18573         if(!this.isExpanded()){
18574             return;
18575         }
18576         
18577         this.list.hide();
18578         
18579         this.hasFocus = false;
18580         
18581         if(this.tickable){
18582             this.okBtn.hide();
18583             this.cancelBtn.hide();
18584             this.trigger.show();
18585             
18586             if(this.editable){
18587                 this.tickableInputEl().dom.value = '';
18588                 this.tickableInputEl().blur();
18589             }
18590             
18591         }
18592         
18593         Roo.get(document).un('mousedown', this.collapseIf, this);
18594         Roo.get(document).un('mousewheel', this.collapseIf, this);
18595         if (!this.editable) {
18596             Roo.get(document).un('keydown', this.listKeyPress, this);
18597         }
18598         this.fireEvent('collapse', this);
18599         
18600         this.validate();
18601     },
18602
18603     // private
18604     collapseIf : function(e){
18605         var in_combo  = e.within(this.el);
18606         var in_list =  e.within(this.list);
18607         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18608         
18609         if (in_combo || in_list || is_list) {
18610             //e.stopPropagation();
18611             return;
18612         }
18613         
18614         if(this.tickable){
18615             this.onTickableFooterButtonClick(e, false, false);
18616         }
18617
18618         this.collapse();
18619         
18620     },
18621
18622     /**
18623      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18624      */
18625     expand : function(){
18626        
18627         if(this.isExpanded() || !this.hasFocus){
18628             return;
18629         }
18630         
18631         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18632         this.list.setWidth(lw);
18633         
18634         Roo.log('expand');
18635         
18636         this.list.show();
18637         
18638         this.restrictHeight();
18639         
18640         if(this.tickable){
18641             
18642             this.tickItems = Roo.apply([], this.item);
18643             
18644             this.okBtn.show();
18645             this.cancelBtn.show();
18646             this.trigger.hide();
18647             
18648             if(this.editable){
18649                 this.tickableInputEl().focus();
18650             }
18651             
18652         }
18653         
18654         Roo.get(document).on('mousedown', this.collapseIf, this);
18655         Roo.get(document).on('mousewheel', this.collapseIf, this);
18656         if (!this.editable) {
18657             Roo.get(document).on('keydown', this.listKeyPress, this);
18658         }
18659         
18660         this.fireEvent('expand', this);
18661     },
18662
18663     // private
18664     // Implements the default empty TriggerField.onTriggerClick function
18665     onTriggerClick : function(e)
18666     {
18667         Roo.log('trigger click');
18668         
18669         if(this.disabled || !this.triggerList){
18670             return;
18671         }
18672         
18673         this.page = 0;
18674         this.loadNext = false;
18675         
18676         if(this.isExpanded()){
18677             this.collapse();
18678             if (!this.blockFocus) {
18679                 this.inputEl().focus();
18680             }
18681             
18682         }else {
18683             this.hasFocus = true;
18684             if(this.triggerAction == 'all') {
18685                 this.doQuery(this.allQuery, true);
18686             } else {
18687                 this.doQuery(this.getRawValue());
18688             }
18689             if (!this.blockFocus) {
18690                 this.inputEl().focus();
18691             }
18692         }
18693     },
18694     
18695     onTickableTriggerClick : function(e)
18696     {
18697         if(this.disabled){
18698             return;
18699         }
18700         
18701         this.page = 0;
18702         this.loadNext = false;
18703         this.hasFocus = true;
18704         
18705         if(this.triggerAction == 'all') {
18706             this.doQuery(this.allQuery, true);
18707         } else {
18708             this.doQuery(this.getRawValue());
18709         }
18710     },
18711     
18712     onSearchFieldClick : function(e)
18713     {
18714         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18715             this.onTickableFooterButtonClick(e, false, false);
18716             return;
18717         }
18718         
18719         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18720             return;
18721         }
18722         
18723         this.page = 0;
18724         this.loadNext = false;
18725         this.hasFocus = true;
18726         
18727         if(this.triggerAction == 'all') {
18728             this.doQuery(this.allQuery, true);
18729         } else {
18730             this.doQuery(this.getRawValue());
18731         }
18732     },
18733     
18734     listKeyPress : function(e)
18735     {
18736         //Roo.log('listkeypress');
18737         // scroll to first matching element based on key pres..
18738         if (e.isSpecialKey()) {
18739             return false;
18740         }
18741         var k = String.fromCharCode(e.getKey()).toUpperCase();
18742         //Roo.log(k);
18743         var match  = false;
18744         var csel = this.view.getSelectedNodes();
18745         var cselitem = false;
18746         if (csel.length) {
18747             var ix = this.view.indexOf(csel[0]);
18748             cselitem  = this.store.getAt(ix);
18749             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18750                 cselitem = false;
18751             }
18752             
18753         }
18754         
18755         this.store.each(function(v) { 
18756             if (cselitem) {
18757                 // start at existing selection.
18758                 if (cselitem.id == v.id) {
18759                     cselitem = false;
18760                 }
18761                 return true;
18762             }
18763                 
18764             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18765                 match = this.store.indexOf(v);
18766                 return false;
18767             }
18768             return true;
18769         }, this);
18770         
18771         if (match === false) {
18772             return true; // no more action?
18773         }
18774         // scroll to?
18775         this.view.select(match);
18776         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18777         sn.scrollIntoView(sn.dom.parentNode, false);
18778     },
18779     
18780     onViewScroll : function(e, t){
18781         
18782         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){
18783             return;
18784         }
18785         
18786         this.hasQuery = true;
18787         
18788         this.loading = this.list.select('.loading', true).first();
18789         
18790         if(this.loading === null){
18791             this.list.createChild({
18792                 tag: 'div',
18793                 cls: 'loading roo-select2-more-results roo-select2-active',
18794                 html: 'Loading more results...'
18795             });
18796             
18797             this.loading = this.list.select('.loading', true).first();
18798             
18799             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18800             
18801             this.loading.hide();
18802         }
18803         
18804         this.loading.show();
18805         
18806         var _combo = this;
18807         
18808         this.page++;
18809         this.loadNext = true;
18810         
18811         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18812         
18813         return;
18814     },
18815     
18816     addItem : function(o)
18817     {   
18818         var dv = ''; // display value
18819         
18820         if (this.displayField) {
18821             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18822         } else {
18823             // this is an error condition!!!
18824             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18825         }
18826         
18827         if(!dv.length){
18828             return;
18829         }
18830         
18831         var choice = this.choices.createChild({
18832             tag: 'li',
18833             cls: 'roo-select2-search-choice',
18834             cn: [
18835                 {
18836                     tag: 'div',
18837                     html: dv
18838                 },
18839                 {
18840                     tag: 'a',
18841                     href: '#',
18842                     cls: 'roo-select2-search-choice-close fa fa-times',
18843                     tabindex: '-1'
18844                 }
18845             ]
18846             
18847         }, this.searchField);
18848         
18849         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18850         
18851         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18852         
18853         this.item.push(o);
18854         
18855         this.lastData = o;
18856         
18857         this.syncValue();
18858         
18859         this.inputEl().dom.value = '';
18860         
18861         this.validate();
18862     },
18863     
18864     onRemoveItem : function(e, _self, o)
18865     {
18866         e.preventDefault();
18867         
18868         this.lastItem = Roo.apply([], this.item);
18869         
18870         var index = this.item.indexOf(o.data) * 1;
18871         
18872         if( index < 0){
18873             Roo.log('not this item?!');
18874             return;
18875         }
18876         
18877         this.item.splice(index, 1);
18878         o.item.remove();
18879         
18880         this.syncValue();
18881         
18882         this.fireEvent('remove', this, e);
18883         
18884         this.validate();
18885         
18886     },
18887     
18888     syncValue : function()
18889     {
18890         if(!this.item.length){
18891             this.clearValue();
18892             return;
18893         }
18894             
18895         var value = [];
18896         var _this = this;
18897         Roo.each(this.item, function(i){
18898             if(_this.valueField){
18899                 value.push(i[_this.valueField]);
18900                 return;
18901             }
18902
18903             value.push(i);
18904         });
18905
18906         this.value = value.join(',');
18907
18908         if(this.hiddenField){
18909             this.hiddenField.dom.value = this.value;
18910         }
18911         
18912         this.store.fireEvent("datachanged", this.store);
18913         
18914         this.validate();
18915     },
18916     
18917     clearItem : function()
18918     {
18919         if(!this.multiple){
18920             return;
18921         }
18922         
18923         this.item = [];
18924         
18925         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18926            c.remove();
18927         });
18928         
18929         this.syncValue();
18930         
18931         this.validate();
18932         
18933         if(this.tickable && !Roo.isTouch){
18934             this.view.refresh();
18935         }
18936     },
18937     
18938     inputEl: function ()
18939     {
18940         if(Roo.isIOS && this.useNativeIOS){
18941             return this.el.select('select.roo-ios-select', true).first();
18942         }
18943         
18944         if(Roo.isTouch && this.mobileTouchView){
18945             return this.el.select('input.form-control',true).first();
18946         }
18947         
18948         if(this.tickable){
18949             return this.searchField;
18950         }
18951         
18952         return this.el.select('input.form-control',true).first();
18953     },
18954     
18955     onTickableFooterButtonClick : function(e, btn, el)
18956     {
18957         e.preventDefault();
18958         
18959         this.lastItem = Roo.apply([], this.item);
18960         
18961         if(btn && btn.name == 'cancel'){
18962             this.tickItems = Roo.apply([], this.item);
18963             this.collapse();
18964             return;
18965         }
18966         
18967         this.clearItem();
18968         
18969         var _this = this;
18970         
18971         Roo.each(this.tickItems, function(o){
18972             _this.addItem(o);
18973         });
18974         
18975         this.collapse();
18976         
18977     },
18978     
18979     validate : function()
18980     {
18981         if(this.getVisibilityEl().hasClass('hidden')){
18982             return true;
18983         }
18984         
18985         var v = this.getRawValue();
18986         
18987         if(this.multiple){
18988             v = this.getValue();
18989         }
18990         
18991         if(this.disabled || this.allowBlank || v.length){
18992             this.markValid();
18993             return true;
18994         }
18995         
18996         this.markInvalid();
18997         return false;
18998     },
18999     
19000     tickableInputEl : function()
19001     {
19002         if(!this.tickable || !this.editable){
19003             return this.inputEl();
19004         }
19005         
19006         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19007     },
19008     
19009     
19010     getAutoCreateTouchView : function()
19011     {
19012         var id = Roo.id();
19013         
19014         var cfg = {
19015             cls: 'form-group' //input-group
19016         };
19017         
19018         var input =  {
19019             tag: 'input',
19020             id : id,
19021             type : this.inputType,
19022             cls : 'form-control x-combo-noedit',
19023             autocomplete: 'new-password',
19024             placeholder : this.placeholder || '',
19025             readonly : true
19026         };
19027         
19028         if (this.name) {
19029             input.name = this.name;
19030         }
19031         
19032         if (this.size) {
19033             input.cls += ' input-' + this.size;
19034         }
19035         
19036         if (this.disabled) {
19037             input.disabled = true;
19038         }
19039         
19040         var inputblock = {
19041             cls : 'roo-combobox-wrap',
19042             cn : [
19043                 input
19044             ]
19045         };
19046         
19047         if(this.before){
19048             inputblock.cls += ' input-group';
19049             
19050             inputblock.cn.unshift({
19051                 tag :'span',
19052                 cls : 'input-group-addon input-group-prepend input-group-text',
19053                 html : this.before
19054             });
19055         }
19056         
19057         if(this.removable && !this.multiple){
19058             inputblock.cls += ' roo-removable';
19059             
19060             inputblock.cn.push({
19061                 tag: 'button',
19062                 html : 'x',
19063                 cls : 'roo-combo-removable-btn close'
19064             });
19065         }
19066
19067         if(this.hasFeedback && !this.allowBlank){
19068             
19069             inputblock.cls += ' has-feedback';
19070             
19071             inputblock.cn.push({
19072                 tag: 'span',
19073                 cls: 'glyphicon form-control-feedback'
19074             });
19075             
19076         }
19077         
19078         if (this.after) {
19079             
19080             inputblock.cls += (this.before) ? '' : ' input-group';
19081             
19082             inputblock.cn.push({
19083                 tag :'span',
19084                 cls : 'input-group-addon input-group-append input-group-text',
19085                 html : this.after
19086             });
19087         }
19088
19089         
19090         var ibwrap = inputblock;
19091         
19092         if(this.multiple){
19093             ibwrap = {
19094                 tag: 'ul',
19095                 cls: 'roo-select2-choices',
19096                 cn:[
19097                     {
19098                         tag: 'li',
19099                         cls: 'roo-select2-search-field',
19100                         cn: [
19101
19102                             inputblock
19103                         ]
19104                     }
19105                 ]
19106             };
19107         
19108             
19109         }
19110         
19111         var combobox = {
19112             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19113             cn: [
19114                 {
19115                     tag: 'input',
19116                     type : 'hidden',
19117                     cls: 'form-hidden-field'
19118                 },
19119                 ibwrap
19120             ]
19121         };
19122         
19123         if(!this.multiple && this.showToggleBtn){
19124             
19125             var caret = {
19126                 cls: 'caret'
19127             };
19128             
19129             if (this.caret != false) {
19130                 caret = {
19131                      tag: 'i',
19132                      cls: 'fa fa-' + this.caret
19133                 };
19134                 
19135             }
19136             
19137             combobox.cn.push({
19138                 tag :'span',
19139                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19140                 cn : [
19141                     Roo.bootstrap.version == 3 ? caret : '',
19142                     {
19143                         tag: 'span',
19144                         cls: 'combobox-clear',
19145                         cn  : [
19146                             {
19147                                 tag : 'i',
19148                                 cls: 'icon-remove'
19149                             }
19150                         ]
19151                     }
19152                 ]
19153
19154             })
19155         }
19156         
19157         if(this.multiple){
19158             combobox.cls += ' roo-select2-container-multi';
19159         }
19160         
19161         var required =  this.allowBlank ?  {
19162                     tag : 'i',
19163                     style: 'display: none'
19164                 } : {
19165                    tag : 'i',
19166                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19167                    tooltip : 'This field is required'
19168                 };
19169         
19170         var align = this.labelAlign || this.parentLabelAlign();
19171         
19172         if (align ==='left' && this.fieldLabel.length) {
19173
19174             cfg.cn = [
19175                 required,
19176                 {
19177                     tag: 'label',
19178                     cls : 'control-label col-form-label',
19179                     html : this.fieldLabel
19180
19181                 },
19182                 {
19183                     cls : 'roo-combobox-wrap ', 
19184                     cn: [
19185                         combobox
19186                     ]
19187                 }
19188             ];
19189             
19190             var labelCfg = cfg.cn[1];
19191             var contentCfg = cfg.cn[2];
19192             
19193
19194             if(this.indicatorpos == 'right'){
19195                 cfg.cn = [
19196                     {
19197                         tag: 'label',
19198                         'for' :  id,
19199                         cls : 'control-label col-form-label',
19200                         cn : [
19201                             {
19202                                 tag : 'span',
19203                                 html : this.fieldLabel
19204                             },
19205                             required
19206                         ]
19207                     },
19208                     {
19209                         cls : "roo-combobox-wrap ",
19210                         cn: [
19211                             combobox
19212                         ]
19213                     }
19214
19215                 ];
19216                 
19217                 labelCfg = cfg.cn[0];
19218                 contentCfg = cfg.cn[1];
19219             }
19220             
19221            
19222             
19223             if(this.labelWidth > 12){
19224                 labelCfg.style = "width: " + this.labelWidth + 'px';
19225             }
19226            
19227             if(this.labelWidth < 13 && this.labelmd == 0){
19228                 this.labelmd = this.labelWidth;
19229             }
19230             
19231             if(this.labellg > 0){
19232                 labelCfg.cls += ' col-lg-' + this.labellg;
19233                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19234             }
19235             
19236             if(this.labelmd > 0){
19237                 labelCfg.cls += ' col-md-' + this.labelmd;
19238                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19239             }
19240             
19241             if(this.labelsm > 0){
19242                 labelCfg.cls += ' col-sm-' + this.labelsm;
19243                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19244             }
19245             
19246             if(this.labelxs > 0){
19247                 labelCfg.cls += ' col-xs-' + this.labelxs;
19248                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19249             }
19250                 
19251                 
19252         } else if ( this.fieldLabel.length) {
19253             cfg.cn = [
19254                required,
19255                 {
19256                     tag: 'label',
19257                     cls : 'control-label',
19258                     html : this.fieldLabel
19259
19260                 },
19261                 {
19262                     cls : '', 
19263                     cn: [
19264                         combobox
19265                     ]
19266                 }
19267             ];
19268             
19269             if(this.indicatorpos == 'right'){
19270                 cfg.cn = [
19271                     {
19272                         tag: 'label',
19273                         cls : 'control-label',
19274                         html : this.fieldLabel,
19275                         cn : [
19276                             required
19277                         ]
19278                     },
19279                     {
19280                         cls : '', 
19281                         cn: [
19282                             combobox
19283                         ]
19284                     }
19285                 ];
19286             }
19287         } else {
19288             cfg.cn = combobox;    
19289         }
19290         
19291         
19292         var settings = this;
19293         
19294         ['xs','sm','md','lg'].map(function(size){
19295             if (settings[size]) {
19296                 cfg.cls += ' col-' + size + '-' + settings[size];
19297             }
19298         });
19299         
19300         return cfg;
19301     },
19302     
19303     initTouchView : function()
19304     {
19305         this.renderTouchView();
19306         
19307         this.touchViewEl.on('scroll', function(){
19308             this.el.dom.scrollTop = 0;
19309         }, this);
19310         
19311         this.originalValue = this.getValue();
19312         
19313         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19314         
19315         this.inputEl().on("click", this.showTouchView, this);
19316         if (this.triggerEl) {
19317             this.triggerEl.on("click", this.showTouchView, this);
19318         }
19319         
19320         
19321         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19322         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19323         
19324         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19325         
19326         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19327         this.store.on('load', this.onTouchViewLoad, this);
19328         this.store.on('loadexception', this.onTouchViewLoadException, this);
19329         
19330         if(this.hiddenName){
19331             
19332             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19333             
19334             this.hiddenField.dom.value =
19335                 this.hiddenValue !== undefined ? this.hiddenValue :
19336                 this.value !== undefined ? this.value : '';
19337         
19338             this.el.dom.removeAttribute('name');
19339             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19340         }
19341         
19342         if(this.multiple){
19343             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19344             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19345         }
19346         
19347         if(this.removable && !this.multiple){
19348             var close = this.closeTriggerEl();
19349             if(close){
19350                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19351                 close.on('click', this.removeBtnClick, this, close);
19352             }
19353         }
19354         /*
19355          * fix the bug in Safari iOS8
19356          */
19357         this.inputEl().on("focus", function(e){
19358             document.activeElement.blur();
19359         }, this);
19360         
19361         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19362         
19363         return;
19364         
19365         
19366     },
19367     
19368     renderTouchView : function()
19369     {
19370         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19371         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19372         
19373         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19374         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19375         
19376         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19377         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19378         this.touchViewBodyEl.setStyle('overflow', 'auto');
19379         
19380         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19381         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19382         
19383         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19384         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19385         
19386     },
19387     
19388     showTouchView : function()
19389     {
19390         if(this.disabled){
19391             return;
19392         }
19393         
19394         this.touchViewHeaderEl.hide();
19395
19396         if(this.modalTitle.length){
19397             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19398             this.touchViewHeaderEl.show();
19399         }
19400
19401         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19402         this.touchViewEl.show();
19403
19404         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19405         
19406         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19407         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19408
19409         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19410
19411         if(this.modalTitle.length){
19412             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19413         }
19414         
19415         this.touchViewBodyEl.setHeight(bodyHeight);
19416
19417         if(this.animate){
19418             var _this = this;
19419             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19420         }else{
19421             this.touchViewEl.addClass(['in','show']);
19422         }
19423         
19424         if(this._touchViewMask){
19425             Roo.get(document.body).addClass("x-body-masked");
19426             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19427             this._touchViewMask.setStyle('z-index', 10000);
19428             this._touchViewMask.addClass('show');
19429         }
19430         
19431         this.doTouchViewQuery();
19432         
19433     },
19434     
19435     hideTouchView : function()
19436     {
19437         this.touchViewEl.removeClass(['in','show']);
19438
19439         if(this.animate){
19440             var _this = this;
19441             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19442         }else{
19443             this.touchViewEl.setStyle('display', 'none');
19444         }
19445         
19446         if(this._touchViewMask){
19447             this._touchViewMask.removeClass('show');
19448             Roo.get(document.body).removeClass("x-body-masked");
19449         }
19450     },
19451     
19452     setTouchViewValue : function()
19453     {
19454         if(this.multiple){
19455             this.clearItem();
19456         
19457             var _this = this;
19458
19459             Roo.each(this.tickItems, function(o){
19460                 this.addItem(o);
19461             }, this);
19462         }
19463         
19464         this.hideTouchView();
19465     },
19466     
19467     doTouchViewQuery : function()
19468     {
19469         var qe = {
19470             query: '',
19471             forceAll: true,
19472             combo: this,
19473             cancel:false
19474         };
19475         
19476         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19477             return false;
19478         }
19479         
19480         if(!this.alwaysQuery || this.mode == 'local'){
19481             this.onTouchViewLoad();
19482             return;
19483         }
19484         
19485         this.store.load();
19486     },
19487     
19488     onTouchViewBeforeLoad : function(combo,opts)
19489     {
19490         return;
19491     },
19492
19493     // private
19494     onTouchViewLoad : function()
19495     {
19496         if(this.store.getCount() < 1){
19497             this.onTouchViewEmptyResults();
19498             return;
19499         }
19500         
19501         this.clearTouchView();
19502         
19503         var rawValue = this.getRawValue();
19504         
19505         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19506         
19507         this.tickItems = [];
19508         
19509         this.store.data.each(function(d, rowIndex){
19510             var row = this.touchViewListGroup.createChild(template);
19511             
19512             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19513                 row.addClass(d.data.cls);
19514             }
19515             
19516             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19517                 var cfg = {
19518                     data : d.data,
19519                     html : d.data[this.displayField]
19520                 };
19521                 
19522                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19523                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19524                 }
19525             }
19526             row.removeClass('selected');
19527             if(!this.multiple && this.valueField &&
19528                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19529             {
19530                 // radio buttons..
19531                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19532                 row.addClass('selected');
19533             }
19534             
19535             if(this.multiple && this.valueField &&
19536                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19537             {
19538                 
19539                 // checkboxes...
19540                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19541                 this.tickItems.push(d.data);
19542             }
19543             
19544             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19545             
19546         }, this);
19547         
19548         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19549         
19550         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19551
19552         if(this.modalTitle.length){
19553             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19554         }
19555
19556         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19557         
19558         if(this.mobile_restrict_height && listHeight < bodyHeight){
19559             this.touchViewBodyEl.setHeight(listHeight);
19560         }
19561         
19562         var _this = this;
19563         
19564         if(firstChecked && listHeight > bodyHeight){
19565             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19566         }
19567         
19568     },
19569     
19570     onTouchViewLoadException : function()
19571     {
19572         this.hideTouchView();
19573     },
19574     
19575     onTouchViewEmptyResults : function()
19576     {
19577         this.clearTouchView();
19578         
19579         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19580         
19581         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19582         
19583     },
19584     
19585     clearTouchView : function()
19586     {
19587         this.touchViewListGroup.dom.innerHTML = '';
19588     },
19589     
19590     onTouchViewClick : function(e, el, o)
19591     {
19592         e.preventDefault();
19593         
19594         var row = o.row;
19595         var rowIndex = o.rowIndex;
19596         
19597         var r = this.store.getAt(rowIndex);
19598         
19599         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19600             
19601             if(!this.multiple){
19602                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19603                     c.dom.removeAttribute('checked');
19604                 }, this);
19605
19606                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19607
19608                 this.setFromData(r.data);
19609
19610                 var close = this.closeTriggerEl();
19611
19612                 if(close){
19613                     close.show();
19614                 }
19615
19616                 this.hideTouchView();
19617
19618                 this.fireEvent('select', this, r, rowIndex);
19619
19620                 return;
19621             }
19622
19623             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19624                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19625                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19626                 return;
19627             }
19628
19629             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19630             this.addItem(r.data);
19631             this.tickItems.push(r.data);
19632         }
19633     },
19634     
19635     getAutoCreateNativeIOS : function()
19636     {
19637         var cfg = {
19638             cls: 'form-group' //input-group,
19639         };
19640         
19641         var combobox =  {
19642             tag: 'select',
19643             cls : 'roo-ios-select'
19644         };
19645         
19646         if (this.name) {
19647             combobox.name = this.name;
19648         }
19649         
19650         if (this.disabled) {
19651             combobox.disabled = true;
19652         }
19653         
19654         var settings = this;
19655         
19656         ['xs','sm','md','lg'].map(function(size){
19657             if (settings[size]) {
19658                 cfg.cls += ' col-' + size + '-' + settings[size];
19659             }
19660         });
19661         
19662         cfg.cn = combobox;
19663         
19664         return cfg;
19665         
19666     },
19667     
19668     initIOSView : function()
19669     {
19670         this.store.on('load', this.onIOSViewLoad, this);
19671         
19672         return;
19673     },
19674     
19675     onIOSViewLoad : function()
19676     {
19677         if(this.store.getCount() < 1){
19678             return;
19679         }
19680         
19681         this.clearIOSView();
19682         
19683         if(this.allowBlank) {
19684             
19685             var default_text = '-- SELECT --';
19686             
19687             if(this.placeholder.length){
19688                 default_text = this.placeholder;
19689             }
19690             
19691             if(this.emptyTitle.length){
19692                 default_text += ' - ' + this.emptyTitle + ' -';
19693             }
19694             
19695             var opt = this.inputEl().createChild({
19696                 tag: 'option',
19697                 value : 0,
19698                 html : default_text
19699             });
19700             
19701             var o = {};
19702             o[this.valueField] = 0;
19703             o[this.displayField] = default_text;
19704             
19705             this.ios_options.push({
19706                 data : o,
19707                 el : opt
19708             });
19709             
19710         }
19711         
19712         this.store.data.each(function(d, rowIndex){
19713             
19714             var html = '';
19715             
19716             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19717                 html = d.data[this.displayField];
19718             }
19719             
19720             var value = '';
19721             
19722             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19723                 value = d.data[this.valueField];
19724             }
19725             
19726             var option = {
19727                 tag: 'option',
19728                 value : value,
19729                 html : html
19730             };
19731             
19732             if(this.value == d.data[this.valueField]){
19733                 option['selected'] = true;
19734             }
19735             
19736             var opt = this.inputEl().createChild(option);
19737             
19738             this.ios_options.push({
19739                 data : d.data,
19740                 el : opt
19741             });
19742             
19743         }, this);
19744         
19745         this.inputEl().on('change', function(){
19746            this.fireEvent('select', this);
19747         }, this);
19748         
19749     },
19750     
19751     clearIOSView: function()
19752     {
19753         this.inputEl().dom.innerHTML = '';
19754         
19755         this.ios_options = [];
19756     },
19757     
19758     setIOSValue: function(v)
19759     {
19760         this.value = v;
19761         
19762         if(!this.ios_options){
19763             return;
19764         }
19765         
19766         Roo.each(this.ios_options, function(opts){
19767            
19768            opts.el.dom.removeAttribute('selected');
19769            
19770            if(opts.data[this.valueField] != v){
19771                return;
19772            }
19773            
19774            opts.el.dom.setAttribute('selected', true);
19775            
19776         }, this);
19777     }
19778
19779     /** 
19780     * @cfg {Boolean} grow 
19781     * @hide 
19782     */
19783     /** 
19784     * @cfg {Number} growMin 
19785     * @hide 
19786     */
19787     /** 
19788     * @cfg {Number} growMax 
19789     * @hide 
19790     */
19791     /**
19792      * @hide
19793      * @method autoSize
19794      */
19795 });
19796
19797 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19798     
19799     header : {
19800         tag: 'div',
19801         cls: 'modal-header',
19802         cn: [
19803             {
19804                 tag: 'h4',
19805                 cls: 'modal-title'
19806             }
19807         ]
19808     },
19809     
19810     body : {
19811         tag: 'div',
19812         cls: 'modal-body',
19813         cn: [
19814             {
19815                 tag: 'ul',
19816                 cls: 'list-group'
19817             }
19818         ]
19819     },
19820     
19821     listItemRadio : {
19822         tag: 'li',
19823         cls: 'list-group-item',
19824         cn: [
19825             {
19826                 tag: 'span',
19827                 cls: 'roo-combobox-list-group-item-value'
19828             },
19829             {
19830                 tag: 'div',
19831                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19832                 cn: [
19833                     {
19834                         tag: 'input',
19835                         type: 'radio'
19836                     },
19837                     {
19838                         tag: 'label'
19839                     }
19840                 ]
19841             }
19842         ]
19843     },
19844     
19845     listItemCheckbox : {
19846         tag: 'li',
19847         cls: 'list-group-item',
19848         cn: [
19849             {
19850                 tag: 'span',
19851                 cls: 'roo-combobox-list-group-item-value'
19852             },
19853             {
19854                 tag: 'div',
19855                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19856                 cn: [
19857                     {
19858                         tag: 'input',
19859                         type: 'checkbox'
19860                     },
19861                     {
19862                         tag: 'label'
19863                     }
19864                 ]
19865             }
19866         ]
19867     },
19868     
19869     emptyResult : {
19870         tag: 'div',
19871         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19872     },
19873     
19874     footer : {
19875         tag: 'div',
19876         cls: 'modal-footer',
19877         cn: [
19878             {
19879                 tag: 'div',
19880                 cls: 'row',
19881                 cn: [
19882                     {
19883                         tag: 'div',
19884                         cls: 'col-xs-6 text-left',
19885                         cn: {
19886                             tag: 'button',
19887                             cls: 'btn btn-danger roo-touch-view-cancel',
19888                             html: 'Cancel'
19889                         }
19890                     },
19891                     {
19892                         tag: 'div',
19893                         cls: 'col-xs-6 text-right',
19894                         cn: {
19895                             tag: 'button',
19896                             cls: 'btn btn-success roo-touch-view-ok',
19897                             html: 'OK'
19898                         }
19899                     }
19900                 ]
19901             }
19902         ]
19903         
19904     }
19905 });
19906
19907 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19908     
19909     touchViewTemplate : {
19910         tag: 'div',
19911         cls: 'modal fade roo-combobox-touch-view',
19912         cn: [
19913             {
19914                 tag: 'div',
19915                 cls: 'modal-dialog',
19916                 style : 'position:fixed', // we have to fix position....
19917                 cn: [
19918                     {
19919                         tag: 'div',
19920                         cls: 'modal-content',
19921                         cn: [
19922                             Roo.bootstrap.form.ComboBox.header,
19923                             Roo.bootstrap.form.ComboBox.body,
19924                             Roo.bootstrap.form.ComboBox.footer
19925                         ]
19926                     }
19927                 ]
19928             }
19929         ]
19930     }
19931 });/*
19932  * Based on:
19933  * Ext JS Library 1.1.1
19934  * Copyright(c) 2006-2007, Ext JS, LLC.
19935  *
19936  * Originally Released Under LGPL - original licence link has changed is not relivant.
19937  *
19938  * Fork - LGPL
19939  * <script type="text/javascript">
19940  */
19941
19942 /**
19943  * @class Roo.View
19944  * @extends Roo.util.Observable
19945  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19946  * This class also supports single and multi selection modes. <br>
19947  * Create a data model bound view:
19948  <pre><code>
19949  var store = new Roo.data.Store(...);
19950
19951  var view = new Roo.View({
19952     el : "my-element",
19953     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19954  
19955     singleSelect: true,
19956     selectedClass: "ydataview-selected",
19957     store: store
19958  });
19959
19960  // listen for node click?
19961  view.on("click", function(vw, index, node, e){
19962  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19963  });
19964
19965  // load XML data
19966  dataModel.load("foobar.xml");
19967  </code></pre>
19968  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19969  * <br><br>
19970  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19971  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19972  * 
19973  * Note: old style constructor is still suported (container, template, config)
19974  * 
19975  * @constructor
19976  * Create a new View
19977  * @param {Object} config The config object
19978  * 
19979  */
19980 Roo.View = function(config, depreciated_tpl, depreciated_config){
19981     
19982     this.parent = false;
19983     
19984     if (typeof(depreciated_tpl) == 'undefined') {
19985         // new way.. - universal constructor.
19986         Roo.apply(this, config);
19987         this.el  = Roo.get(this.el);
19988     } else {
19989         // old format..
19990         this.el  = Roo.get(config);
19991         this.tpl = depreciated_tpl;
19992         Roo.apply(this, depreciated_config);
19993     }
19994     this.wrapEl  = this.el.wrap().wrap();
19995     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19996     
19997     
19998     if(typeof(this.tpl) == "string"){
19999         this.tpl = new Roo.Template(this.tpl);
20000     } else {
20001         // support xtype ctors..
20002         this.tpl = new Roo.factory(this.tpl, Roo);
20003     }
20004     
20005     
20006     this.tpl.compile();
20007     
20008     /** @private */
20009     this.addEvents({
20010         /**
20011          * @event beforeclick
20012          * Fires before a click is processed. Returns false to cancel the default action.
20013          * @param {Roo.View} this
20014          * @param {Number} index The index of the target node
20015          * @param {HTMLElement} node The target node
20016          * @param {Roo.EventObject} e The raw event object
20017          */
20018             "beforeclick" : true,
20019         /**
20020          * @event click
20021          * Fires when a template node is clicked.
20022          * @param {Roo.View} this
20023          * @param {Number} index The index of the target node
20024          * @param {HTMLElement} node The target node
20025          * @param {Roo.EventObject} e The raw event object
20026          */
20027             "click" : true,
20028         /**
20029          * @event dblclick
20030          * Fires when a template node is double clicked.
20031          * @param {Roo.View} this
20032          * @param {Number} index The index of the target node
20033          * @param {HTMLElement} node The target node
20034          * @param {Roo.EventObject} e The raw event object
20035          */
20036             "dblclick" : true,
20037         /**
20038          * @event contextmenu
20039          * Fires when a template node is right clicked.
20040          * @param {Roo.View} this
20041          * @param {Number} index The index of the target node
20042          * @param {HTMLElement} node The target node
20043          * @param {Roo.EventObject} e The raw event object
20044          */
20045             "contextmenu" : true,
20046         /**
20047          * @event selectionchange
20048          * Fires when the selected nodes change.
20049          * @param {Roo.View} this
20050          * @param {Array} selections Array of the selected nodes
20051          */
20052             "selectionchange" : true,
20053     
20054         /**
20055          * @event beforeselect
20056          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20057          * @param {Roo.View} this
20058          * @param {HTMLElement} node The node to be selected
20059          * @param {Array} selections Array of currently selected nodes
20060          */
20061             "beforeselect" : true,
20062         /**
20063          * @event preparedata
20064          * Fires on every row to render, to allow you to change the data.
20065          * @param {Roo.View} this
20066          * @param {Object} data to be rendered (change this)
20067          */
20068           "preparedata" : true
20069           
20070           
20071         });
20072
20073
20074
20075     this.el.on({
20076         "click": this.onClick,
20077         "dblclick": this.onDblClick,
20078         "contextmenu": this.onContextMenu,
20079         scope:this
20080     });
20081
20082     this.selections = [];
20083     this.nodes = [];
20084     this.cmp = new Roo.CompositeElementLite([]);
20085     if(this.store){
20086         this.store = Roo.factory(this.store, Roo.data);
20087         this.setStore(this.store, true);
20088     }
20089     
20090     if ( this.footer && this.footer.xtype) {
20091            
20092          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20093         
20094         this.footer.dataSource = this.store;
20095         this.footer.container = fctr;
20096         this.footer = Roo.factory(this.footer, Roo);
20097         fctr.insertFirst(this.el);
20098         
20099         // this is a bit insane - as the paging toolbar seems to detach the el..
20100 //        dom.parentNode.parentNode.parentNode
20101          // they get detached?
20102     }
20103     
20104     
20105     Roo.View.superclass.constructor.call(this);
20106     
20107     
20108 };
20109
20110 Roo.extend(Roo.View, Roo.util.Observable, {
20111     
20112      /**
20113      * @cfg {Roo.data.Store} store Data store to load data from.
20114      */
20115     store : false,
20116     
20117     /**
20118      * @cfg {String|Roo.Element} el The container element.
20119      */
20120     el : '',
20121     
20122     /**
20123      * @cfg {String|Roo.Template} tpl The template used by this View 
20124      */
20125     tpl : false,
20126     /**
20127      * @cfg {String} dataName the named area of the template to use as the data area
20128      *                          Works with domtemplates roo-name="name"
20129      */
20130     dataName: false,
20131     /**
20132      * @cfg {String} selectedClass The css class to add to selected nodes
20133      */
20134     selectedClass : "x-view-selected",
20135      /**
20136      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20137      */
20138     emptyText : "",
20139     
20140     /**
20141      * @cfg {String} text to display on mask (default Loading)
20142      */
20143     mask : false,
20144     /**
20145      * @cfg {Boolean} multiSelect Allow multiple selection
20146      */
20147     multiSelect : false,
20148     /**
20149      * @cfg {Boolean} singleSelect Allow single selection
20150      */
20151     singleSelect:  false,
20152     
20153     /**
20154      * @cfg {Boolean} toggleSelect - selecting 
20155      */
20156     toggleSelect : false,
20157     
20158     /**
20159      * @cfg {Boolean} tickable - selecting 
20160      */
20161     tickable : false,
20162     
20163     /**
20164      * Returns the element this view is bound to.
20165      * @return {Roo.Element}
20166      */
20167     getEl : function(){
20168         return this.wrapEl;
20169     },
20170     
20171     
20172
20173     /**
20174      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20175      */
20176     refresh : function(){
20177         //Roo.log('refresh');
20178         var t = this.tpl;
20179         
20180         // if we are using something like 'domtemplate', then
20181         // the what gets used is:
20182         // t.applySubtemplate(NAME, data, wrapping data..)
20183         // the outer template then get' applied with
20184         //     the store 'extra data'
20185         // and the body get's added to the
20186         //      roo-name="data" node?
20187         //      <span class='roo-tpl-{name}'></span> ?????
20188         
20189         
20190         
20191         this.clearSelections();
20192         this.el.update("");
20193         var html = [];
20194         var records = this.store.getRange();
20195         if(records.length < 1) {
20196             
20197             // is this valid??  = should it render a template??
20198             
20199             this.el.update(this.emptyText);
20200             return;
20201         }
20202         var el = this.el;
20203         if (this.dataName) {
20204             this.el.update(t.apply(this.store.meta)); //????
20205             el = this.el.child('.roo-tpl-' + this.dataName);
20206         }
20207         
20208         for(var i = 0, len = records.length; i < len; i++){
20209             var data = this.prepareData(records[i].data, i, records[i]);
20210             this.fireEvent("preparedata", this, data, i, records[i]);
20211             
20212             var d = Roo.apply({}, data);
20213             
20214             if(this.tickable){
20215                 Roo.apply(d, {'roo-id' : Roo.id()});
20216                 
20217                 var _this = this;
20218             
20219                 Roo.each(this.parent.item, function(item){
20220                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20221                         return;
20222                     }
20223                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20224                 });
20225             }
20226             
20227             html[html.length] = Roo.util.Format.trim(
20228                 this.dataName ?
20229                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20230                     t.apply(d)
20231             );
20232         }
20233         
20234         
20235         
20236         el.update(html.join(""));
20237         this.nodes = el.dom.childNodes;
20238         this.updateIndexes(0);
20239     },
20240     
20241
20242     /**
20243      * Function to override to reformat the data that is sent to
20244      * the template for each node.
20245      * DEPRICATED - use the preparedata event handler.
20246      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20247      * a JSON object for an UpdateManager bound view).
20248      */
20249     prepareData : function(data, index, record)
20250     {
20251         this.fireEvent("preparedata", this, data, index, record);
20252         return data;
20253     },
20254
20255     onUpdate : function(ds, record){
20256         // Roo.log('on update');   
20257         this.clearSelections();
20258         var index = this.store.indexOf(record);
20259         var n = this.nodes[index];
20260         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20261         n.parentNode.removeChild(n);
20262         this.updateIndexes(index, index);
20263     },
20264
20265     
20266     
20267 // --------- FIXME     
20268     onAdd : function(ds, records, index)
20269     {
20270         //Roo.log(['on Add', ds, records, index] );        
20271         this.clearSelections();
20272         if(this.nodes.length == 0){
20273             this.refresh();
20274             return;
20275         }
20276         var n = this.nodes[index];
20277         for(var i = 0, len = records.length; i < len; i++){
20278             var d = this.prepareData(records[i].data, i, records[i]);
20279             if(n){
20280                 this.tpl.insertBefore(n, d);
20281             }else{
20282                 
20283                 this.tpl.append(this.el, d);
20284             }
20285         }
20286         this.updateIndexes(index);
20287     },
20288
20289     onRemove : function(ds, record, index){
20290        // Roo.log('onRemove');
20291         this.clearSelections();
20292         var el = this.dataName  ?
20293             this.el.child('.roo-tpl-' + this.dataName) :
20294             this.el; 
20295         
20296         el.dom.removeChild(this.nodes[index]);
20297         this.updateIndexes(index);
20298     },
20299
20300     /**
20301      * Refresh an individual node.
20302      * @param {Number} index
20303      */
20304     refreshNode : function(index){
20305         this.onUpdate(this.store, this.store.getAt(index));
20306     },
20307
20308     updateIndexes : function(startIndex, endIndex){
20309         var ns = this.nodes;
20310         startIndex = startIndex || 0;
20311         endIndex = endIndex || ns.length - 1;
20312         for(var i = startIndex; i <= endIndex; i++){
20313             ns[i].nodeIndex = i;
20314         }
20315     },
20316
20317     /**
20318      * Changes the data store this view uses and refresh the view.
20319      * @param {Store} store
20320      */
20321     setStore : function(store, initial){
20322         if(!initial && this.store){
20323             this.store.un("datachanged", this.refresh);
20324             this.store.un("add", this.onAdd);
20325             this.store.un("remove", this.onRemove);
20326             this.store.un("update", this.onUpdate);
20327             this.store.un("clear", this.refresh);
20328             this.store.un("beforeload", this.onBeforeLoad);
20329             this.store.un("load", this.onLoad);
20330             this.store.un("loadexception", this.onLoad);
20331         }
20332         if(store){
20333           
20334             store.on("datachanged", this.refresh, this);
20335             store.on("add", this.onAdd, this);
20336             store.on("remove", this.onRemove, this);
20337             store.on("update", this.onUpdate, this);
20338             store.on("clear", this.refresh, this);
20339             store.on("beforeload", this.onBeforeLoad, this);
20340             store.on("load", this.onLoad, this);
20341             store.on("loadexception", this.onLoad, this);
20342         }
20343         
20344         if(store){
20345             this.refresh();
20346         }
20347     },
20348     /**
20349      * onbeforeLoad - masks the loading area.
20350      *
20351      */
20352     onBeforeLoad : function(store,opts)
20353     {
20354          //Roo.log('onBeforeLoad');   
20355         if (!opts.add) {
20356             this.el.update("");
20357         }
20358         this.el.mask(this.mask ? this.mask : "Loading" ); 
20359     },
20360     onLoad : function ()
20361     {
20362         this.el.unmask();
20363     },
20364     
20365
20366     /**
20367      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20368      * @param {HTMLElement} node
20369      * @return {HTMLElement} The template node
20370      */
20371     findItemFromChild : function(node){
20372         var el = this.dataName  ?
20373             this.el.child('.roo-tpl-' + this.dataName,true) :
20374             this.el.dom; 
20375         
20376         if(!node || node.parentNode == el){
20377                     return node;
20378             }
20379             var p = node.parentNode;
20380             while(p && p != el){
20381             if(p.parentNode == el){
20382                 return p;
20383             }
20384             p = p.parentNode;
20385         }
20386             return null;
20387     },
20388
20389     /** @ignore */
20390     onClick : function(e){
20391         var item = this.findItemFromChild(e.getTarget());
20392         if(item){
20393             var index = this.indexOf(item);
20394             if(this.onItemClick(item, index, e) !== false){
20395                 this.fireEvent("click", this, index, item, e);
20396             }
20397         }else{
20398             this.clearSelections();
20399         }
20400     },
20401
20402     /** @ignore */
20403     onContextMenu : function(e){
20404         var item = this.findItemFromChild(e.getTarget());
20405         if(item){
20406             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20407         }
20408     },
20409
20410     /** @ignore */
20411     onDblClick : function(e){
20412         var item = this.findItemFromChild(e.getTarget());
20413         if(item){
20414             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20415         }
20416     },
20417
20418     onItemClick : function(item, index, e)
20419     {
20420         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20421             return false;
20422         }
20423         if (this.toggleSelect) {
20424             var m = this.isSelected(item) ? 'unselect' : 'select';
20425             //Roo.log(m);
20426             var _t = this;
20427             _t[m](item, true, false);
20428             return true;
20429         }
20430         if(this.multiSelect || this.singleSelect){
20431             if(this.multiSelect && e.shiftKey && this.lastSelection){
20432                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20433             }else{
20434                 this.select(item, this.multiSelect && e.ctrlKey);
20435                 this.lastSelection = item;
20436             }
20437             
20438             if(!this.tickable){
20439                 e.preventDefault();
20440             }
20441             
20442         }
20443         return true;
20444     },
20445
20446     /**
20447      * Get the number of selected nodes.
20448      * @return {Number}
20449      */
20450     getSelectionCount : function(){
20451         return this.selections.length;
20452     },
20453
20454     /**
20455      * Get the currently selected nodes.
20456      * @return {Array} An array of HTMLElements
20457      */
20458     getSelectedNodes : function(){
20459         return this.selections;
20460     },
20461
20462     /**
20463      * Get the indexes of the selected nodes.
20464      * @return {Array}
20465      */
20466     getSelectedIndexes : function(){
20467         var indexes = [], s = this.selections;
20468         for(var i = 0, len = s.length; i < len; i++){
20469             indexes.push(s[i].nodeIndex);
20470         }
20471         return indexes;
20472     },
20473
20474     /**
20475      * Clear all selections
20476      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20477      */
20478     clearSelections : function(suppressEvent){
20479         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20480             this.cmp.elements = this.selections;
20481             this.cmp.removeClass(this.selectedClass);
20482             this.selections = [];
20483             if(!suppressEvent){
20484                 this.fireEvent("selectionchange", this, this.selections);
20485             }
20486         }
20487     },
20488
20489     /**
20490      * Returns true if the passed node is selected
20491      * @param {HTMLElement/Number} node The node or node index
20492      * @return {Boolean}
20493      */
20494     isSelected : function(node){
20495         var s = this.selections;
20496         if(s.length < 1){
20497             return false;
20498         }
20499         node = this.getNode(node);
20500         return s.indexOf(node) !== -1;
20501     },
20502
20503     /**
20504      * Selects nodes.
20505      * @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
20506      * @param {Boolean} keepExisting (optional) true to keep existing selections
20507      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20508      */
20509     select : function(nodeInfo, keepExisting, suppressEvent){
20510         if(nodeInfo instanceof Array){
20511             if(!keepExisting){
20512                 this.clearSelections(true);
20513             }
20514             for(var i = 0, len = nodeInfo.length; i < len; i++){
20515                 this.select(nodeInfo[i], true, true);
20516             }
20517             return;
20518         } 
20519         var node = this.getNode(nodeInfo);
20520         if(!node || this.isSelected(node)){
20521             return; // already selected.
20522         }
20523         if(!keepExisting){
20524             this.clearSelections(true);
20525         }
20526         
20527         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20528             Roo.fly(node).addClass(this.selectedClass);
20529             this.selections.push(node);
20530             if(!suppressEvent){
20531                 this.fireEvent("selectionchange", this, this.selections);
20532             }
20533         }
20534         
20535         
20536     },
20537       /**
20538      * Unselects nodes.
20539      * @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
20540      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20541      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20542      */
20543     unselect : function(nodeInfo, keepExisting, suppressEvent)
20544     {
20545         if(nodeInfo instanceof Array){
20546             Roo.each(this.selections, function(s) {
20547                 this.unselect(s, nodeInfo);
20548             }, this);
20549             return;
20550         }
20551         var node = this.getNode(nodeInfo);
20552         if(!node || !this.isSelected(node)){
20553             //Roo.log("not selected");
20554             return; // not selected.
20555         }
20556         // fireevent???
20557         var ns = [];
20558         Roo.each(this.selections, function(s) {
20559             if (s == node ) {
20560                 Roo.fly(node).removeClass(this.selectedClass);
20561
20562                 return;
20563             }
20564             ns.push(s);
20565         },this);
20566         
20567         this.selections= ns;
20568         this.fireEvent("selectionchange", this, this.selections);
20569     },
20570
20571     /**
20572      * Gets a template node.
20573      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20574      * @return {HTMLElement} The node or null if it wasn't found
20575      */
20576     getNode : function(nodeInfo){
20577         if(typeof nodeInfo == "string"){
20578             return document.getElementById(nodeInfo);
20579         }else if(typeof nodeInfo == "number"){
20580             return this.nodes[nodeInfo];
20581         }
20582         return nodeInfo;
20583     },
20584
20585     /**
20586      * Gets a range template nodes.
20587      * @param {Number} startIndex
20588      * @param {Number} endIndex
20589      * @return {Array} An array of nodes
20590      */
20591     getNodes : function(start, end){
20592         var ns = this.nodes;
20593         start = start || 0;
20594         end = typeof end == "undefined" ? ns.length - 1 : end;
20595         var nodes = [];
20596         if(start <= end){
20597             for(var i = start; i <= end; i++){
20598                 nodes.push(ns[i]);
20599             }
20600         } else{
20601             for(var i = start; i >= end; i--){
20602                 nodes.push(ns[i]);
20603             }
20604         }
20605         return nodes;
20606     },
20607
20608     /**
20609      * Finds the index of the passed node
20610      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20611      * @return {Number} The index of the node or -1
20612      */
20613     indexOf : function(node){
20614         node = this.getNode(node);
20615         if(typeof node.nodeIndex == "number"){
20616             return node.nodeIndex;
20617         }
20618         var ns = this.nodes;
20619         for(var i = 0, len = ns.length; i < len; i++){
20620             if(ns[i] == node){
20621                 return i;
20622             }
20623         }
20624         return -1;
20625     }
20626 });
20627 /*
20628  * - LGPL
20629  *
20630  * based on jquery fullcalendar
20631  * 
20632  */
20633
20634 Roo.bootstrap = Roo.bootstrap || {};
20635 /**
20636  * @class Roo.bootstrap.Calendar
20637  * @extends Roo.bootstrap.Component
20638  * Bootstrap Calendar class
20639  * @cfg {Boolean} loadMask (true|false) default false
20640  * @cfg {Object} header generate the user specific header of the calendar, default false
20641
20642  * @constructor
20643  * Create a new Container
20644  * @param {Object} config The config object
20645  */
20646
20647
20648
20649 Roo.bootstrap.Calendar = function(config){
20650     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20651      this.addEvents({
20652         /**
20653              * @event select
20654              * Fires when a date is selected
20655              * @param {DatePicker} this
20656              * @param {Date} date The selected date
20657              */
20658         'select': true,
20659         /**
20660              * @event monthchange
20661              * Fires when the displayed month changes 
20662              * @param {DatePicker} this
20663              * @param {Date} date The selected month
20664              */
20665         'monthchange': true,
20666         /**
20667              * @event evententer
20668              * Fires when mouse over an event
20669              * @param {Calendar} this
20670              * @param {event} Event
20671              */
20672         'evententer': true,
20673         /**
20674              * @event eventleave
20675              * Fires when the mouse leaves an
20676              * @param {Calendar} this
20677              * @param {event}
20678              */
20679         'eventleave': true,
20680         /**
20681              * @event eventclick
20682              * Fires when the mouse click an
20683              * @param {Calendar} this
20684              * @param {event}
20685              */
20686         'eventclick': true
20687         
20688     });
20689
20690 };
20691
20692 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20693     
20694           /**
20695      * @cfg {Roo.data.Store} store
20696      * The data source for the calendar
20697      */
20698         store : false,
20699      /**
20700      * @cfg {Number} startDay
20701      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20702      */
20703     startDay : 0,
20704     
20705     loadMask : false,
20706     
20707     header : false,
20708       
20709     getAutoCreate : function(){
20710         
20711         
20712         var fc_button = function(name, corner, style, content ) {
20713             return Roo.apply({},{
20714                 tag : 'span',
20715                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20716                          (corner.length ?
20717                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20718                             ''
20719                         ),
20720                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20721                 unselectable: 'on'
20722             });
20723         };
20724         
20725         var header = {};
20726         
20727         if(!this.header){
20728             header = {
20729                 tag : 'table',
20730                 cls : 'fc-header',
20731                 style : 'width:100%',
20732                 cn : [
20733                     {
20734                         tag: 'tr',
20735                         cn : [
20736                             {
20737                                 tag : 'td',
20738                                 cls : 'fc-header-left',
20739                                 cn : [
20740                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20741                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20742                                     { tag: 'span', cls: 'fc-header-space' },
20743                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20744
20745
20746                                 ]
20747                             },
20748
20749                             {
20750                                 tag : 'td',
20751                                 cls : 'fc-header-center',
20752                                 cn : [
20753                                     {
20754                                         tag: 'span',
20755                                         cls: 'fc-header-title',
20756                                         cn : {
20757                                             tag: 'H2',
20758                                             html : 'month / year'
20759                                         }
20760                                     }
20761
20762                                 ]
20763                             },
20764                             {
20765                                 tag : 'td',
20766                                 cls : 'fc-header-right',
20767                                 cn : [
20768                               /*      fc_button('month', 'left', '', 'month' ),
20769                                     fc_button('week', '', '', 'week' ),
20770                                     fc_button('day', 'right', '', 'day' )
20771                                 */    
20772
20773                                 ]
20774                             }
20775
20776                         ]
20777                     }
20778                 ]
20779             };
20780         }
20781         
20782         header = this.header;
20783         
20784        
20785         var cal_heads = function() {
20786             var ret = [];
20787             // fixme - handle this.
20788             
20789             for (var i =0; i < Date.dayNames.length; i++) {
20790                 var d = Date.dayNames[i];
20791                 ret.push({
20792                     tag: 'th',
20793                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20794                     html : d.substring(0,3)
20795                 });
20796                 
20797             }
20798             ret[0].cls += ' fc-first';
20799             ret[6].cls += ' fc-last';
20800             return ret;
20801         };
20802         var cal_cell = function(n) {
20803             return  {
20804                 tag: 'td',
20805                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20806                 cn : [
20807                     {
20808                         cn : [
20809                             {
20810                                 cls: 'fc-day-number',
20811                                 html: 'D'
20812                             },
20813                             {
20814                                 cls: 'fc-day-content',
20815                              
20816                                 cn : [
20817                                      {
20818                                         style: 'position: relative;' // height: 17px;
20819                                     }
20820                                 ]
20821                             }
20822                             
20823                             
20824                         ]
20825                     }
20826                 ]
20827                 
20828             }
20829         };
20830         var cal_rows = function() {
20831             
20832             var ret = [];
20833             for (var r = 0; r < 6; r++) {
20834                 var row= {
20835                     tag : 'tr',
20836                     cls : 'fc-week',
20837                     cn : []
20838                 };
20839                 
20840                 for (var i =0; i < Date.dayNames.length; i++) {
20841                     var d = Date.dayNames[i];
20842                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20843
20844                 }
20845                 row.cn[0].cls+=' fc-first';
20846                 row.cn[0].cn[0].style = 'min-height:90px';
20847                 row.cn[6].cls+=' fc-last';
20848                 ret.push(row);
20849                 
20850             }
20851             ret[0].cls += ' fc-first';
20852             ret[4].cls += ' fc-prev-last';
20853             ret[5].cls += ' fc-last';
20854             return ret;
20855             
20856         };
20857         
20858         var cal_table = {
20859             tag: 'table',
20860             cls: 'fc-border-separate',
20861             style : 'width:100%',
20862             cellspacing  : 0,
20863             cn : [
20864                 { 
20865                     tag: 'thead',
20866                     cn : [
20867                         { 
20868                             tag: 'tr',
20869                             cls : 'fc-first fc-last',
20870                             cn : cal_heads()
20871                         }
20872                     ]
20873                 },
20874                 { 
20875                     tag: 'tbody',
20876                     cn : cal_rows()
20877                 }
20878                   
20879             ]
20880         };
20881          
20882          var cfg = {
20883             cls : 'fc fc-ltr',
20884             cn : [
20885                 header,
20886                 {
20887                     cls : 'fc-content',
20888                     style : "position: relative;",
20889                     cn : [
20890                         {
20891                             cls : 'fc-view fc-view-month fc-grid',
20892                             style : 'position: relative',
20893                             unselectable : 'on',
20894                             cn : [
20895                                 {
20896                                     cls : 'fc-event-container',
20897                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20898                                 },
20899                                 cal_table
20900                             ]
20901                         }
20902                     ]
20903     
20904                 }
20905            ] 
20906             
20907         };
20908         
20909          
20910         
20911         return cfg;
20912     },
20913     
20914     
20915     initEvents : function()
20916     {
20917         if(!this.store){
20918             throw "can not find store for calendar";
20919         }
20920         
20921         var mark = {
20922             tag: "div",
20923             cls:"x-dlg-mask",
20924             style: "text-align:center",
20925             cn: [
20926                 {
20927                     tag: "div",
20928                     style: "background-color:white;width:50%;margin:250 auto",
20929                     cn: [
20930                         {
20931                             tag: "img",
20932                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20933                         },
20934                         {
20935                             tag: "span",
20936                             html: "Loading"
20937                         }
20938                         
20939                     ]
20940                 }
20941             ]
20942         };
20943         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20944         
20945         var size = this.el.select('.fc-content', true).first().getSize();
20946         this.maskEl.setSize(size.width, size.height);
20947         this.maskEl.enableDisplayMode("block");
20948         if(!this.loadMask){
20949             this.maskEl.hide();
20950         }
20951         
20952         this.store = Roo.factory(this.store, Roo.data);
20953         this.store.on('load', this.onLoad, this);
20954         this.store.on('beforeload', this.onBeforeLoad, this);
20955         
20956         this.resize();
20957         
20958         this.cells = this.el.select('.fc-day',true);
20959         //Roo.log(this.cells);
20960         this.textNodes = this.el.query('.fc-day-number');
20961         this.cells.addClassOnOver('fc-state-hover');
20962         
20963         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20964         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20965         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20966         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20967         
20968         this.on('monthchange', this.onMonthChange, this);
20969         
20970         this.update(new Date().clearTime());
20971     },
20972     
20973     resize : function() {
20974         var sz  = this.el.getSize();
20975         
20976         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20977         this.el.select('.fc-day-content div',true).setHeight(34);
20978     },
20979     
20980     
20981     // private
20982     showPrevMonth : function(e){
20983         this.update(this.activeDate.add("mo", -1));
20984     },
20985     showToday : function(e){
20986         this.update(new Date().clearTime());
20987     },
20988     // private
20989     showNextMonth : function(e){
20990         this.update(this.activeDate.add("mo", 1));
20991     },
20992
20993     // private
20994     showPrevYear : function(){
20995         this.update(this.activeDate.add("y", -1));
20996     },
20997
20998     // private
20999     showNextYear : function(){
21000         this.update(this.activeDate.add("y", 1));
21001     },
21002
21003     
21004    // private
21005     update : function(date)
21006     {
21007         var vd = this.activeDate;
21008         this.activeDate = date;
21009 //        if(vd && this.el){
21010 //            var t = date.getTime();
21011 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21012 //                Roo.log('using add remove');
21013 //                
21014 //                this.fireEvent('monthchange', this, date);
21015 //                
21016 //                this.cells.removeClass("fc-state-highlight");
21017 //                this.cells.each(function(c){
21018 //                   if(c.dateValue == t){
21019 //                       c.addClass("fc-state-highlight");
21020 //                       setTimeout(function(){
21021 //                            try{c.dom.firstChild.focus();}catch(e){}
21022 //                       }, 50);
21023 //                       return false;
21024 //                   }
21025 //                   return true;
21026 //                });
21027 //                return;
21028 //            }
21029 //        }
21030         
21031         var days = date.getDaysInMonth();
21032         
21033         var firstOfMonth = date.getFirstDateOfMonth();
21034         var startingPos = firstOfMonth.getDay()-this.startDay;
21035         
21036         if(startingPos < this.startDay){
21037             startingPos += 7;
21038         }
21039         
21040         var pm = date.add(Date.MONTH, -1);
21041         var prevStart = pm.getDaysInMonth()-startingPos;
21042 //        
21043         this.cells = this.el.select('.fc-day',true);
21044         this.textNodes = this.el.query('.fc-day-number');
21045         this.cells.addClassOnOver('fc-state-hover');
21046         
21047         var cells = this.cells.elements;
21048         var textEls = this.textNodes;
21049         
21050         Roo.each(cells, function(cell){
21051             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21052         });
21053         
21054         days += startingPos;
21055
21056         // convert everything to numbers so it's fast
21057         var day = 86400000;
21058         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21059         //Roo.log(d);
21060         //Roo.log(pm);
21061         //Roo.log(prevStart);
21062         
21063         var today = new Date().clearTime().getTime();
21064         var sel = date.clearTime().getTime();
21065         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21066         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21067         var ddMatch = this.disabledDatesRE;
21068         var ddText = this.disabledDatesText;
21069         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21070         var ddaysText = this.disabledDaysText;
21071         var format = this.format;
21072         
21073         var setCellClass = function(cal, cell){
21074             cell.row = 0;
21075             cell.events = [];
21076             cell.more = [];
21077             //Roo.log('set Cell Class');
21078             cell.title = "";
21079             var t = d.getTime();
21080             
21081             //Roo.log(d);
21082             
21083             cell.dateValue = t;
21084             if(t == today){
21085                 cell.className += " fc-today";
21086                 cell.className += " fc-state-highlight";
21087                 cell.title = cal.todayText;
21088             }
21089             if(t == sel){
21090                 // disable highlight in other month..
21091                 //cell.className += " fc-state-highlight";
21092                 
21093             }
21094             // disabling
21095             if(t < min) {
21096                 cell.className = " fc-state-disabled";
21097                 cell.title = cal.minText;
21098                 return;
21099             }
21100             if(t > max) {
21101                 cell.className = " fc-state-disabled";
21102                 cell.title = cal.maxText;
21103                 return;
21104             }
21105             if(ddays){
21106                 if(ddays.indexOf(d.getDay()) != -1){
21107                     cell.title = ddaysText;
21108                     cell.className = " fc-state-disabled";
21109                 }
21110             }
21111             if(ddMatch && format){
21112                 var fvalue = d.dateFormat(format);
21113                 if(ddMatch.test(fvalue)){
21114                     cell.title = ddText.replace("%0", fvalue);
21115                     cell.className = " fc-state-disabled";
21116                 }
21117             }
21118             
21119             if (!cell.initialClassName) {
21120                 cell.initialClassName = cell.dom.className;
21121             }
21122             
21123             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21124         };
21125
21126         var i = 0;
21127         
21128         for(; i < startingPos; i++) {
21129             textEls[i].innerHTML = (++prevStart);
21130             d.setDate(d.getDate()+1);
21131             
21132             cells[i].className = "fc-past fc-other-month";
21133             setCellClass(this, cells[i]);
21134         }
21135         
21136         var intDay = 0;
21137         
21138         for(; i < days; i++){
21139             intDay = i - startingPos + 1;
21140             textEls[i].innerHTML = (intDay);
21141             d.setDate(d.getDate()+1);
21142             
21143             cells[i].className = ''; // "x-date-active";
21144             setCellClass(this, cells[i]);
21145         }
21146         var extraDays = 0;
21147         
21148         for(; i < 42; i++) {
21149             textEls[i].innerHTML = (++extraDays);
21150             d.setDate(d.getDate()+1);
21151             
21152             cells[i].className = "fc-future fc-other-month";
21153             setCellClass(this, cells[i]);
21154         }
21155         
21156         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21157         
21158         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21159         
21160         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21161         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21162         
21163         if(totalRows != 6){
21164             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21165             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21166         }
21167         
21168         this.fireEvent('monthchange', this, date);
21169         
21170         
21171         /*
21172         if(!this.internalRender){
21173             var main = this.el.dom.firstChild;
21174             var w = main.offsetWidth;
21175             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21176             Roo.fly(main).setWidth(w);
21177             this.internalRender = true;
21178             // opera does not respect the auto grow header center column
21179             // then, after it gets a width opera refuses to recalculate
21180             // without a second pass
21181             if(Roo.isOpera && !this.secondPass){
21182                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21183                 this.secondPass = true;
21184                 this.update.defer(10, this, [date]);
21185             }
21186         }
21187         */
21188         
21189     },
21190     
21191     findCell : function(dt) {
21192         dt = dt.clearTime().getTime();
21193         var ret = false;
21194         this.cells.each(function(c){
21195             //Roo.log("check " +c.dateValue + '?=' + dt);
21196             if(c.dateValue == dt){
21197                 ret = c;
21198                 return false;
21199             }
21200             return true;
21201         });
21202         
21203         return ret;
21204     },
21205     
21206     findCells : function(ev) {
21207         var s = ev.start.clone().clearTime().getTime();
21208        // Roo.log(s);
21209         var e= ev.end.clone().clearTime().getTime();
21210        // Roo.log(e);
21211         var ret = [];
21212         this.cells.each(function(c){
21213              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21214             
21215             if(c.dateValue > e){
21216                 return ;
21217             }
21218             if(c.dateValue < s){
21219                 return ;
21220             }
21221             ret.push(c);
21222         });
21223         
21224         return ret;    
21225     },
21226     
21227 //    findBestRow: function(cells)
21228 //    {
21229 //        var ret = 0;
21230 //        
21231 //        for (var i =0 ; i < cells.length;i++) {
21232 //            ret  = Math.max(cells[i].rows || 0,ret);
21233 //        }
21234 //        return ret;
21235 //        
21236 //    },
21237     
21238     
21239     addItem : function(ev)
21240     {
21241         // look for vertical location slot in
21242         var cells = this.findCells(ev);
21243         
21244 //        ev.row = this.findBestRow(cells);
21245         
21246         // work out the location.
21247         
21248         var crow = false;
21249         var rows = [];
21250         for(var i =0; i < cells.length; i++) {
21251             
21252             cells[i].row = cells[0].row;
21253             
21254             if(i == 0){
21255                 cells[i].row = cells[i].row + 1;
21256             }
21257             
21258             if (!crow) {
21259                 crow = {
21260                     start : cells[i],
21261                     end :  cells[i]
21262                 };
21263                 continue;
21264             }
21265             if (crow.start.getY() == cells[i].getY()) {
21266                 // on same row.
21267                 crow.end = cells[i];
21268                 continue;
21269             }
21270             // different row.
21271             rows.push(crow);
21272             crow = {
21273                 start: cells[i],
21274                 end : cells[i]
21275             };
21276             
21277         }
21278         
21279         rows.push(crow);
21280         ev.els = [];
21281         ev.rows = rows;
21282         ev.cells = cells;
21283         
21284         cells[0].events.push(ev);
21285         
21286         this.calevents.push(ev);
21287     },
21288     
21289     clearEvents: function() {
21290         
21291         if(!this.calevents){
21292             return;
21293         }
21294         
21295         Roo.each(this.cells.elements, function(c){
21296             c.row = 0;
21297             c.events = [];
21298             c.more = [];
21299         });
21300         
21301         Roo.each(this.calevents, function(e) {
21302             Roo.each(e.els, function(el) {
21303                 el.un('mouseenter' ,this.onEventEnter, this);
21304                 el.un('mouseleave' ,this.onEventLeave, this);
21305                 el.remove();
21306             },this);
21307         },this);
21308         
21309         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21310             e.remove();
21311         });
21312         
21313     },
21314     
21315     renderEvents: function()
21316     {   
21317         var _this = this;
21318         
21319         this.cells.each(function(c) {
21320             
21321             if(c.row < 5){
21322                 return;
21323             }
21324             
21325             var ev = c.events;
21326             
21327             var r = 4;
21328             if(c.row != c.events.length){
21329                 r = 4 - (4 - (c.row - c.events.length));
21330             }
21331             
21332             c.events = ev.slice(0, r);
21333             c.more = ev.slice(r);
21334             
21335             if(c.more.length && c.more.length == 1){
21336                 c.events.push(c.more.pop());
21337             }
21338             
21339             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21340             
21341         });
21342             
21343         this.cells.each(function(c) {
21344             
21345             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21346             
21347             
21348             for (var e = 0; e < c.events.length; e++){
21349                 var ev = c.events[e];
21350                 var rows = ev.rows;
21351                 
21352                 for(var i = 0; i < rows.length; i++) {
21353                 
21354                     // how many rows should it span..
21355
21356                     var  cfg = {
21357                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21358                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21359
21360                         unselectable : "on",
21361                         cn : [
21362                             {
21363                                 cls: 'fc-event-inner',
21364                                 cn : [
21365     //                                {
21366     //                                  tag:'span',
21367     //                                  cls: 'fc-event-time',
21368     //                                  html : cells.length > 1 ? '' : ev.time
21369     //                                },
21370                                     {
21371                                       tag:'span',
21372                                       cls: 'fc-event-title',
21373                                       html : String.format('{0}', ev.title)
21374                                     }
21375
21376
21377                                 ]
21378                             },
21379                             {
21380                                 cls: 'ui-resizable-handle ui-resizable-e',
21381                                 html : '&nbsp;&nbsp;&nbsp'
21382                             }
21383
21384                         ]
21385                     };
21386
21387                     if (i == 0) {
21388                         cfg.cls += ' fc-event-start';
21389                     }
21390                     if ((i+1) == rows.length) {
21391                         cfg.cls += ' fc-event-end';
21392                     }
21393
21394                     var ctr = _this.el.select('.fc-event-container',true).first();
21395                     var cg = ctr.createChild(cfg);
21396
21397                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21398                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21399
21400                     var r = (c.more.length) ? 1 : 0;
21401                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21402                     cg.setWidth(ebox.right - sbox.x -2);
21403
21404                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21405                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21406                     cg.on('click', _this.onEventClick, _this, ev);
21407
21408                     ev.els.push(cg);
21409                     
21410                 }
21411                 
21412             }
21413             
21414             
21415             if(c.more.length){
21416                 var  cfg = {
21417                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21418                     style : 'position: absolute',
21419                     unselectable : "on",
21420                     cn : [
21421                         {
21422                             cls: 'fc-event-inner',
21423                             cn : [
21424                                 {
21425                                   tag:'span',
21426                                   cls: 'fc-event-title',
21427                                   html : 'More'
21428                                 }
21429
21430
21431                             ]
21432                         },
21433                         {
21434                             cls: 'ui-resizable-handle ui-resizable-e',
21435                             html : '&nbsp;&nbsp;&nbsp'
21436                         }
21437
21438                     ]
21439                 };
21440
21441                 var ctr = _this.el.select('.fc-event-container',true).first();
21442                 var cg = ctr.createChild(cfg);
21443
21444                 var sbox = c.select('.fc-day-content',true).first().getBox();
21445                 var ebox = c.select('.fc-day-content',true).first().getBox();
21446                 //Roo.log(cg);
21447                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21448                 cg.setWidth(ebox.right - sbox.x -2);
21449
21450                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21451                 
21452             }
21453             
21454         });
21455         
21456         
21457         
21458     },
21459     
21460     onEventEnter: function (e, el,event,d) {
21461         this.fireEvent('evententer', this, el, event);
21462     },
21463     
21464     onEventLeave: function (e, el,event,d) {
21465         this.fireEvent('eventleave', this, el, event);
21466     },
21467     
21468     onEventClick: function (e, el,event,d) {
21469         this.fireEvent('eventclick', this, el, event);
21470     },
21471     
21472     onMonthChange: function () {
21473         this.store.load();
21474     },
21475     
21476     onMoreEventClick: function(e, el, more)
21477     {
21478         var _this = this;
21479         
21480         this.calpopover.placement = 'right';
21481         this.calpopover.setTitle('More');
21482         
21483         this.calpopover.setContent('');
21484         
21485         var ctr = this.calpopover.el.select('.popover-content', true).first();
21486         
21487         Roo.each(more, function(m){
21488             var cfg = {
21489                 cls : 'fc-event-hori fc-event-draggable',
21490                 html : m.title
21491             };
21492             var cg = ctr.createChild(cfg);
21493             
21494             cg.on('click', _this.onEventClick, _this, m);
21495         });
21496         
21497         this.calpopover.show(el);
21498         
21499         
21500     },
21501     
21502     onLoad: function () 
21503     {   
21504         this.calevents = [];
21505         var cal = this;
21506         
21507         if(this.store.getCount() > 0){
21508             this.store.data.each(function(d){
21509                cal.addItem({
21510                     id : d.data.id,
21511                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21512                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21513                     time : d.data.start_time,
21514                     title : d.data.title,
21515                     description : d.data.description,
21516                     venue : d.data.venue
21517                 });
21518             });
21519         }
21520         
21521         this.renderEvents();
21522         
21523         if(this.calevents.length && this.loadMask){
21524             this.maskEl.hide();
21525         }
21526     },
21527     
21528     onBeforeLoad: function()
21529     {
21530         this.clearEvents();
21531         if(this.loadMask){
21532             this.maskEl.show();
21533         }
21534     }
21535 });
21536
21537  
21538  /*
21539  * - LGPL
21540  *
21541  * element
21542  * 
21543  */
21544
21545 /**
21546  * @class Roo.bootstrap.Popover
21547  * @extends Roo.bootstrap.Component
21548  * @parent none builder
21549  * @children Roo.bootstrap.Component
21550  * Bootstrap Popover class
21551  * @cfg {String} html contents of the popover   (or false to use children..)
21552  * @cfg {String} title of popover (or false to hide)
21553  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21554  * @cfg {String} trigger click || hover (or false to trigger manually)
21555  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21556  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21557  *      - if false and it has a 'parent' then it will be automatically added to that element
21558  *      - if string - Roo.get  will be called 
21559  * @cfg {Number} delay - delay before showing
21560  
21561  * @constructor
21562  * Create a new Popover
21563  * @param {Object} config The config object
21564  */
21565
21566 Roo.bootstrap.Popover = function(config){
21567     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21568     
21569     this.addEvents({
21570         // raw events
21571          /**
21572          * @event show
21573          * After the popover show
21574          * 
21575          * @param {Roo.bootstrap.Popover} this
21576          */
21577         "show" : true,
21578         /**
21579          * @event hide
21580          * After the popover hide
21581          * 
21582          * @param {Roo.bootstrap.Popover} this
21583          */
21584         "hide" : true
21585     });
21586 };
21587
21588 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21589     
21590     title: false,
21591     html: false,
21592     
21593     placement : 'right',
21594     trigger : 'hover', // hover
21595     modal : false,
21596     delay : 0,
21597     
21598     over: false,
21599     
21600     can_build_overlaid : false,
21601     
21602     maskEl : false, // the mask element
21603     headerEl : false,
21604     contentEl : false,
21605     alignEl : false, // when show is called with an element - this get's stored.
21606     
21607     getChildContainer : function()
21608     {
21609         return this.contentEl;
21610         
21611     },
21612     getPopoverHeader : function()
21613     {
21614         this.title = true; // flag not to hide it..
21615         this.headerEl.addClass('p-0');
21616         return this.headerEl
21617     },
21618     
21619     
21620     getAutoCreate : function(){
21621          
21622         var cfg = {
21623            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21624            style: 'display:block',
21625            cn : [
21626                 {
21627                     cls : 'arrow'
21628                 },
21629                 {
21630                     cls : 'popover-inner ',
21631                     cn : [
21632                         {
21633                             tag: 'h3',
21634                             cls: 'popover-title popover-header',
21635                             html : this.title === false ? '' : this.title
21636                         },
21637                         {
21638                             cls : 'popover-content popover-body '  + (this.cls || ''),
21639                             html : this.html || ''
21640                         }
21641                     ]
21642                     
21643                 }
21644            ]
21645         };
21646         
21647         return cfg;
21648     },
21649     /**
21650      * @param {string} the title
21651      */
21652     setTitle: function(str)
21653     {
21654         this.title = str;
21655         if (this.el) {
21656             this.headerEl.dom.innerHTML = str;
21657         }
21658         
21659     },
21660     /**
21661      * @param {string} the body content
21662      */
21663     setContent: function(str)
21664     {
21665         this.html = str;
21666         if (this.contentEl) {
21667             this.contentEl.dom.innerHTML = str;
21668         }
21669         
21670     },
21671     // as it get's added to the bottom of the page.
21672     onRender : function(ct, position)
21673     {
21674         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21675         
21676         
21677         
21678         if(!this.el){
21679             var cfg = Roo.apply({},  this.getAutoCreate());
21680             cfg.id = Roo.id();
21681             
21682             if (this.cls) {
21683                 cfg.cls += ' ' + this.cls;
21684             }
21685             if (this.style) {
21686                 cfg.style = this.style;
21687             }
21688             //Roo.log("adding to ");
21689             this.el = Roo.get(document.body).createChild(cfg, position);
21690 //            Roo.log(this.el);
21691         }
21692         
21693         this.contentEl = this.el.select('.popover-content',true).first();
21694         this.headerEl =  this.el.select('.popover-title',true).first();
21695         
21696         var nitems = [];
21697         if(typeof(this.items) != 'undefined'){
21698             var items = this.items;
21699             delete this.items;
21700
21701             for(var i =0;i < items.length;i++) {
21702                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21703             }
21704         }
21705
21706         this.items = nitems;
21707         
21708         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21709         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21710         
21711         
21712         
21713         this.initEvents();
21714     },
21715     
21716     resizeMask : function()
21717     {
21718         this.maskEl.setSize(
21719             Roo.lib.Dom.getViewWidth(true),
21720             Roo.lib.Dom.getViewHeight(true)
21721         );
21722     },
21723     
21724     initEvents : function()
21725     {
21726         
21727         if (!this.modal) { 
21728             Roo.bootstrap.Popover.register(this);
21729         }
21730          
21731         this.arrowEl = this.el.select('.arrow',true).first();
21732         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21733         this.el.enableDisplayMode('block');
21734         this.el.hide();
21735  
21736         
21737         if (this.over === false && !this.parent()) {
21738             return; 
21739         }
21740         if (this.triggers === false) {
21741             return;
21742         }
21743          
21744         // support parent
21745         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21746         var triggers = this.trigger ? this.trigger.split(' ') : [];
21747         Roo.each(triggers, function(trigger) {
21748         
21749             if (trigger == 'click') {
21750                 on_el.on('click', this.toggle, this);
21751             } else if (trigger != 'manual') {
21752                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21753                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21754       
21755                 on_el.on(eventIn  ,this.enter, this);
21756                 on_el.on(eventOut, this.leave, this);
21757             }
21758         }, this);
21759     },
21760     
21761     
21762     // private
21763     timeout : null,
21764     hoverState : null,
21765     
21766     toggle : function () {
21767         this.hoverState == 'in' ? this.leave() : this.enter();
21768     },
21769     
21770     enter : function () {
21771         
21772         clearTimeout(this.timeout);
21773     
21774         this.hoverState = 'in';
21775     
21776         if (!this.delay || !this.delay.show) {
21777             this.show();
21778             return;
21779         }
21780         var _t = this;
21781         this.timeout = setTimeout(function () {
21782             if (_t.hoverState == 'in') {
21783                 _t.show();
21784             }
21785         }, this.delay.show)
21786     },
21787     
21788     leave : function() {
21789         clearTimeout(this.timeout);
21790     
21791         this.hoverState = 'out';
21792     
21793         if (!this.delay || !this.delay.hide) {
21794             this.hide();
21795             return;
21796         }
21797         var _t = this;
21798         this.timeout = setTimeout(function () {
21799             if (_t.hoverState == 'out') {
21800                 _t.hide();
21801             }
21802         }, this.delay.hide)
21803     },
21804     
21805     /**
21806      * update the position of the dialog
21807      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21808      * 
21809      *
21810      */
21811     
21812     doAlign : function()
21813     {
21814         
21815         if (this.alignEl) {
21816             this.updatePosition(this.placement, true);
21817              
21818         } else {
21819             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21820             var es = this.el.getSize();
21821             var x = Roo.lib.Dom.getViewWidth()/2;
21822             var y = Roo.lib.Dom.getViewHeight()/2;
21823             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21824             
21825         }
21826
21827          
21828          
21829         
21830         
21831     },
21832     
21833     /**
21834      * Show the popover
21835      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21836      * @param {string} (left|right|top|bottom) position
21837      */
21838     show : function (on_el, placement)
21839     {
21840         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21841         on_el = on_el || false; // default to false
21842          
21843         if (!on_el) {
21844             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21845                 on_el = this.parent().el;
21846             } else if (this.over) {
21847                 on_el = Roo.get(this.over);
21848             }
21849             
21850         }
21851         
21852         this.alignEl = Roo.get( on_el );
21853
21854         if (!this.el) {
21855             this.render(document.body);
21856         }
21857         
21858         
21859          
21860         
21861         if (this.title === false) {
21862             this.headerEl.hide();
21863         }
21864         
21865        
21866         this.el.show();
21867         this.el.dom.style.display = 'block';
21868          
21869         this.doAlign();
21870         
21871         //var arrow = this.el.select('.arrow',true).first();
21872         //arrow.set(align[2], 
21873         
21874         this.el.addClass('in');
21875         
21876          
21877         
21878         this.hoverState = 'in';
21879         
21880         if (this.modal) {
21881             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21882             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21883             this.maskEl.dom.style.display = 'block';
21884             this.maskEl.addClass('show');
21885         }
21886         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21887  
21888         this.fireEvent('show', this);
21889         
21890     },
21891     /**
21892      * fire this manually after loading a grid in the table for example
21893      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21894      * @param {Boolean} try and move it if we cant get right position.
21895      */
21896     updatePosition : function(placement, try_move)
21897     {
21898         // allow for calling with no parameters
21899         placement = placement   ? placement :  this.placement;
21900         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21901         
21902         this.el.removeClass([
21903             'fade','top','bottom', 'left', 'right','in',
21904             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21905         ]);
21906         this.el.addClass(placement + ' bs-popover-' + placement);
21907         
21908         if (!this.alignEl ) {
21909             return false;
21910         }
21911         
21912         switch (placement) {
21913             case 'right':
21914                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21915                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21916                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21917                     //normal display... or moved up/down.
21918                     this.el.setXY(offset);
21919                     var xy = this.alignEl.getAnchorXY('tr', false);
21920                     xy[0]+=2;xy[1]+=5;
21921                     this.arrowEl.setXY(xy);
21922                     return true;
21923                 }
21924                 // continue through...
21925                 return this.updatePosition('left', false);
21926                 
21927             
21928             case 'left':
21929                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21930                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21931                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21932                     //normal display... or moved up/down.
21933                     this.el.setXY(offset);
21934                     var xy = this.alignEl.getAnchorXY('tl', false);
21935                     xy[0]-=10;xy[1]+=5; // << fix me
21936                     this.arrowEl.setXY(xy);
21937                     return true;
21938                 }
21939                 // call self...
21940                 return this.updatePosition('right', false);
21941             
21942             case 'top':
21943                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21944                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21945                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21946                     //normal display... or moved up/down.
21947                     this.el.setXY(offset);
21948                     var xy = this.alignEl.getAnchorXY('t', false);
21949                     xy[1]-=10; // << fix me
21950                     this.arrowEl.setXY(xy);
21951                     return true;
21952                 }
21953                 // fall through
21954                return this.updatePosition('bottom', false);
21955             
21956             case 'bottom':
21957                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21958                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21959                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21960                     //normal display... or moved up/down.
21961                     this.el.setXY(offset);
21962                     var xy = this.alignEl.getAnchorXY('b', false);
21963                      xy[1]+=2; // << fix me
21964                     this.arrowEl.setXY(xy);
21965                     return true;
21966                 }
21967                 // fall through
21968                 return this.updatePosition('top', false);
21969                 
21970             
21971         }
21972         
21973         
21974         return false;
21975     },
21976     
21977     hide : function()
21978     {
21979         this.el.setXY([0,0]);
21980         this.el.removeClass('in');
21981         this.el.hide();
21982         this.hoverState = null;
21983         this.maskEl.hide(); // always..
21984         this.fireEvent('hide', this);
21985     }
21986     
21987 });
21988
21989
21990 Roo.apply(Roo.bootstrap.Popover, {
21991
21992     alignment : {
21993         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21994         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21995         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21996         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21997     },
21998     
21999     zIndex : 20001,
22000
22001     clickHander : false,
22002     
22003     
22004
22005     onMouseDown : function(e)
22006     {
22007         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22008             /// what is nothing is showing..
22009             this.hideAll();
22010         }
22011          
22012     },
22013     
22014     
22015     popups : [],
22016     
22017     register : function(popup)
22018     {
22019         if (!Roo.bootstrap.Popover.clickHandler) {
22020             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22021         }
22022         // hide other popups.
22023         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22024         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22025         this.hideAll(); //<< why?
22026         //this.popups.push(popup);
22027     },
22028     hideAll : function()
22029     {
22030         this.popups.forEach(function(p) {
22031             p.hide();
22032         });
22033     },
22034     onShow : function() {
22035         Roo.bootstrap.Popover.popups.push(this);
22036     },
22037     onHide : function() {
22038         Roo.bootstrap.Popover.popups.remove(this);
22039     } 
22040
22041 });
22042 /**
22043  * @class Roo.bootstrap.PopoverNav
22044  * @extends Roo.bootstrap.nav.Simplebar
22045  * @parent Roo.bootstrap.Popover
22046  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22047  * @licence LGPL
22048  * Bootstrap Popover header navigation class
22049  * FIXME? should this go under nav?
22050  *
22051  * 
22052  * @constructor
22053  * Create a new Popover Header Navigation 
22054  * @param {Object} config The config object
22055  */
22056
22057 Roo.bootstrap.PopoverNav = function(config){
22058     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22059 };
22060
22061 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22062     
22063     
22064     container_method : 'getPopoverHeader' 
22065     
22066      
22067     
22068     
22069    
22070 });
22071
22072  
22073
22074  /*
22075  * - LGPL
22076  *
22077  * Progress
22078  * 
22079  */
22080
22081 /**
22082  * @class Roo.bootstrap.Progress
22083  * @extends Roo.bootstrap.Component
22084  * @children Roo.bootstrap.ProgressBar
22085  * Bootstrap Progress class
22086  * @cfg {Boolean} striped striped of the progress bar
22087  * @cfg {Boolean} active animated of the progress bar
22088  * 
22089  * 
22090  * @constructor
22091  * Create a new Progress
22092  * @param {Object} config The config object
22093  */
22094
22095 Roo.bootstrap.Progress = function(config){
22096     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22097 };
22098
22099 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22100     
22101     striped : false,
22102     active: false,
22103     
22104     getAutoCreate : function(){
22105         var cfg = {
22106             tag: 'div',
22107             cls: 'progress'
22108         };
22109         
22110         
22111         if(this.striped){
22112             cfg.cls += ' progress-striped';
22113         }
22114       
22115         if(this.active){
22116             cfg.cls += ' active';
22117         }
22118         
22119         
22120         return cfg;
22121     }
22122    
22123 });
22124
22125  
22126
22127  /*
22128  * - LGPL
22129  *
22130  * ProgressBar
22131  * 
22132  */
22133
22134 /**
22135  * @class Roo.bootstrap.ProgressBar
22136  * @extends Roo.bootstrap.Component
22137  * Bootstrap ProgressBar class
22138  * @cfg {Number} aria_valuenow aria-value now
22139  * @cfg {Number} aria_valuemin aria-value min
22140  * @cfg {Number} aria_valuemax aria-value max
22141  * @cfg {String} label label for the progress bar
22142  * @cfg {String} panel (success | info | warning | danger )
22143  * @cfg {String} role role of the progress bar
22144  * @cfg {String} sr_only text
22145  * 
22146  * 
22147  * @constructor
22148  * Create a new ProgressBar
22149  * @param {Object} config The config object
22150  */
22151
22152 Roo.bootstrap.ProgressBar = function(config){
22153     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22154 };
22155
22156 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22157     
22158     aria_valuenow : 0,
22159     aria_valuemin : 0,
22160     aria_valuemax : 100,
22161     label : false,
22162     panel : false,
22163     role : false,
22164     sr_only: false,
22165     
22166     getAutoCreate : function()
22167     {
22168         
22169         var cfg = {
22170             tag: 'div',
22171             cls: 'progress-bar',
22172             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22173         };
22174         
22175         if(this.sr_only){
22176             cfg.cn = {
22177                 tag: 'span',
22178                 cls: 'sr-only',
22179                 html: this.sr_only
22180             }
22181         }
22182         
22183         if(this.role){
22184             cfg.role = this.role;
22185         }
22186         
22187         if(this.aria_valuenow){
22188             cfg['aria-valuenow'] = this.aria_valuenow;
22189         }
22190         
22191         if(this.aria_valuemin){
22192             cfg['aria-valuemin'] = this.aria_valuemin;
22193         }
22194         
22195         if(this.aria_valuemax){
22196             cfg['aria-valuemax'] = this.aria_valuemax;
22197         }
22198         
22199         if(this.label && !this.sr_only){
22200             cfg.html = this.label;
22201         }
22202         
22203         if(this.panel){
22204             cfg.cls += ' progress-bar-' + this.panel;
22205         }
22206         
22207         return cfg;
22208     },
22209     
22210     update : function(aria_valuenow)
22211     {
22212         this.aria_valuenow = aria_valuenow;
22213         
22214         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22215     }
22216    
22217 });
22218
22219  
22220
22221  /**
22222  * @class Roo.bootstrap.TabGroup
22223  * @extends Roo.bootstrap.Column
22224  * @children Roo.bootstrap.TabPanel
22225  * Bootstrap Column class
22226  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22227  * @cfg {Boolean} carousel true to make the group behave like a carousel
22228  * @cfg {Boolean} bullets show bullets for the panels
22229  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22230  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22231  * @cfg {Boolean} showarrow (true|false) show arrow default true
22232  * 
22233  * @constructor
22234  * Create a new TabGroup
22235  * @param {Object} config The config object
22236  */
22237
22238 Roo.bootstrap.TabGroup = function(config){
22239     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22240     if (!this.navId) {
22241         this.navId = Roo.id();
22242     }
22243     this.tabs = [];
22244     Roo.bootstrap.TabGroup.register(this);
22245     
22246 };
22247
22248 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22249     
22250     carousel : false,
22251     transition : false,
22252     bullets : 0,
22253     timer : 0,
22254     autoslide : false,
22255     slideFn : false,
22256     slideOnTouch : false,
22257     showarrow : true,
22258     
22259     getAutoCreate : function()
22260     {
22261         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22262         
22263         cfg.cls += ' tab-content';
22264         
22265         if (this.carousel) {
22266             cfg.cls += ' carousel slide';
22267             
22268             cfg.cn = [{
22269                cls : 'carousel-inner',
22270                cn : []
22271             }];
22272         
22273             if(this.bullets  && !Roo.isTouch){
22274                 
22275                 var bullets = {
22276                     cls : 'carousel-bullets',
22277                     cn : []
22278                 };
22279                
22280                 if(this.bullets_cls){
22281                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22282                 }
22283                 
22284                 bullets.cn.push({
22285                     cls : 'clear'
22286                 });
22287                 
22288                 cfg.cn[0].cn.push(bullets);
22289             }
22290             
22291             if(this.showarrow){
22292                 cfg.cn[0].cn.push({
22293                     tag : 'div',
22294                     class : 'carousel-arrow',
22295                     cn : [
22296                         {
22297                             tag : 'div',
22298                             class : 'carousel-prev',
22299                             cn : [
22300                                 {
22301                                     tag : 'i',
22302                                     class : 'fa fa-chevron-left'
22303                                 }
22304                             ]
22305                         },
22306                         {
22307                             tag : 'div',
22308                             class : 'carousel-next',
22309                             cn : [
22310                                 {
22311                                     tag : 'i',
22312                                     class : 'fa fa-chevron-right'
22313                                 }
22314                             ]
22315                         }
22316                     ]
22317                 });
22318             }
22319             
22320         }
22321         
22322         return cfg;
22323     },
22324     
22325     initEvents:  function()
22326     {
22327 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22328 //            this.el.on("touchstart", this.onTouchStart, this);
22329 //        }
22330         
22331         if(this.autoslide){
22332             var _this = this;
22333             
22334             this.slideFn = window.setInterval(function() {
22335                 _this.showPanelNext();
22336             }, this.timer);
22337         }
22338         
22339         if(this.showarrow){
22340             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22341             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22342         }
22343         
22344         
22345     },
22346     
22347 //    onTouchStart : function(e, el, o)
22348 //    {
22349 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22350 //            return;
22351 //        }
22352 //        
22353 //        this.showPanelNext();
22354 //    },
22355     
22356     
22357     getChildContainer : function()
22358     {
22359         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22360     },
22361     
22362     /**
22363     * register a Navigation item
22364     * @param {Roo.bootstrap.nav.Item} the navitem to add
22365     */
22366     register : function(item)
22367     {
22368         this.tabs.push( item);
22369         item.navId = this.navId; // not really needed..
22370         this.addBullet();
22371     
22372     },
22373     
22374     getActivePanel : function()
22375     {
22376         var r = false;
22377         Roo.each(this.tabs, function(t) {
22378             if (t.active) {
22379                 r = t;
22380                 return false;
22381             }
22382             return null;
22383         });
22384         return r;
22385         
22386     },
22387     getPanelByName : function(n)
22388     {
22389         var r = false;
22390         Roo.each(this.tabs, function(t) {
22391             if (t.tabId == n) {
22392                 r = t;
22393                 return false;
22394             }
22395             return null;
22396         });
22397         return r;
22398     },
22399     indexOfPanel : function(p)
22400     {
22401         var r = false;
22402         Roo.each(this.tabs, function(t,i) {
22403             if (t.tabId == p.tabId) {
22404                 r = i;
22405                 return false;
22406             }
22407             return null;
22408         });
22409         return r;
22410     },
22411     /**
22412      * show a specific panel
22413      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22414      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22415      */
22416     showPanel : function (pan)
22417     {
22418         if(this.transition || typeof(pan) == 'undefined'){
22419             Roo.log("waiting for the transitionend");
22420             return false;
22421         }
22422         
22423         if (typeof(pan) == 'number') {
22424             pan = this.tabs[pan];
22425         }
22426         
22427         if (typeof(pan) == 'string') {
22428             pan = this.getPanelByName(pan);
22429         }
22430         
22431         var cur = this.getActivePanel();
22432         
22433         if(!pan || !cur){
22434             Roo.log('pan or acitve pan is undefined');
22435             return false;
22436         }
22437         
22438         if (pan.tabId == this.getActivePanel().tabId) {
22439             return true;
22440         }
22441         
22442         if (false === cur.fireEvent('beforedeactivate')) {
22443             return false;
22444         }
22445         
22446         if(this.bullets > 0 && !Roo.isTouch){
22447             this.setActiveBullet(this.indexOfPanel(pan));
22448         }
22449         
22450         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22451             
22452             //class="carousel-item carousel-item-next carousel-item-left"
22453             
22454             this.transition = true;
22455             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22456             var lr = dir == 'next' ? 'left' : 'right';
22457             pan.el.addClass(dir); // or prev
22458             pan.el.addClass('carousel-item-' + dir); // or prev
22459             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22460             cur.el.addClass(lr); // or right
22461             pan.el.addClass(lr);
22462             cur.el.addClass('carousel-item-' +lr); // or right
22463             pan.el.addClass('carousel-item-' +lr);
22464             
22465             
22466             var _this = this;
22467             cur.el.on('transitionend', function() {
22468                 Roo.log("trans end?");
22469                 
22470                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22471                 pan.setActive(true);
22472                 
22473                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22474                 cur.setActive(false);
22475                 
22476                 _this.transition = false;
22477                 
22478             }, this, { single:  true } );
22479             
22480             return true;
22481         }
22482         
22483         cur.setActive(false);
22484         pan.setActive(true);
22485         
22486         return true;
22487         
22488     },
22489     showPanelNext : function()
22490     {
22491         var i = this.indexOfPanel(this.getActivePanel());
22492         
22493         if (i >= this.tabs.length - 1 && !this.autoslide) {
22494             return;
22495         }
22496         
22497         if (i >= this.tabs.length - 1 && this.autoslide) {
22498             i = -1;
22499         }
22500         
22501         this.showPanel(this.tabs[i+1]);
22502     },
22503     
22504     showPanelPrev : function()
22505     {
22506         var i = this.indexOfPanel(this.getActivePanel());
22507         
22508         if (i  < 1 && !this.autoslide) {
22509             return;
22510         }
22511         
22512         if (i < 1 && this.autoslide) {
22513             i = this.tabs.length;
22514         }
22515         
22516         this.showPanel(this.tabs[i-1]);
22517     },
22518     
22519     
22520     addBullet: function()
22521     {
22522         if(!this.bullets || Roo.isTouch){
22523             return;
22524         }
22525         var ctr = this.el.select('.carousel-bullets',true).first();
22526         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22527         var bullet = ctr.createChild({
22528             cls : 'bullet bullet-' + i
22529         },ctr.dom.lastChild);
22530         
22531         
22532         var _this = this;
22533         
22534         bullet.on('click', (function(e, el, o, ii, t){
22535
22536             e.preventDefault();
22537
22538             this.showPanel(ii);
22539
22540             if(this.autoslide && this.slideFn){
22541                 clearInterval(this.slideFn);
22542                 this.slideFn = window.setInterval(function() {
22543                     _this.showPanelNext();
22544                 }, this.timer);
22545             }
22546
22547         }).createDelegate(this, [i, bullet], true));
22548                 
22549         
22550     },
22551      
22552     setActiveBullet : function(i)
22553     {
22554         if(Roo.isTouch){
22555             return;
22556         }
22557         
22558         Roo.each(this.el.select('.bullet', true).elements, function(el){
22559             el.removeClass('selected');
22560         });
22561
22562         var bullet = this.el.select('.bullet-' + i, true).first();
22563         
22564         if(!bullet){
22565             return;
22566         }
22567         
22568         bullet.addClass('selected');
22569     }
22570     
22571     
22572   
22573 });
22574
22575  
22576
22577  
22578  
22579 Roo.apply(Roo.bootstrap.TabGroup, {
22580     
22581     groups: {},
22582      /**
22583     * register a Navigation Group
22584     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22585     */
22586     register : function(navgrp)
22587     {
22588         this.groups[navgrp.navId] = navgrp;
22589         
22590     },
22591     /**
22592     * fetch a Navigation Group based on the navigation ID
22593     * if one does not exist , it will get created.
22594     * @param {string} the navgroup to add
22595     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22596     */
22597     get: function(navId) {
22598         if (typeof(this.groups[navId]) == 'undefined') {
22599             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22600         }
22601         return this.groups[navId] ;
22602     }
22603     
22604     
22605     
22606 });
22607
22608  /*
22609  * - LGPL
22610  *
22611  * TabPanel
22612  * 
22613  */
22614
22615 /**
22616  * @class Roo.bootstrap.TabPanel
22617  * @extends Roo.bootstrap.Component
22618  * @children Roo.bootstrap.Component
22619  * Bootstrap TabPanel class
22620  * @cfg {Boolean} active panel active
22621  * @cfg {String} html panel content
22622  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22623  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22624  * @cfg {String} href click to link..
22625  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22626  * 
22627  * 
22628  * @constructor
22629  * Create a new TabPanel
22630  * @param {Object} config The config object
22631  */
22632
22633 Roo.bootstrap.TabPanel = function(config){
22634     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22635     this.addEvents({
22636         /**
22637              * @event changed
22638              * Fires when the active status changes
22639              * @param {Roo.bootstrap.TabPanel} this
22640              * @param {Boolean} state the new state
22641             
22642          */
22643         'changed': true,
22644         /**
22645              * @event beforedeactivate
22646              * Fires before a tab is de-activated - can be used to do validation on a form.
22647              * @param {Roo.bootstrap.TabPanel} this
22648              * @return {Boolean} false if there is an error
22649             
22650          */
22651         'beforedeactivate': true
22652      });
22653     
22654     this.tabId = this.tabId || Roo.id();
22655   
22656 };
22657
22658 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22659     
22660     active: false,
22661     html: false,
22662     tabId: false,
22663     navId : false,
22664     href : '',
22665     touchSlide : false,
22666     getAutoCreate : function(){
22667         
22668         
22669         var cfg = {
22670             tag: 'div',
22671             // item is needed for carousel - not sure if it has any effect otherwise
22672             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22673             html: this.html || ''
22674         };
22675         
22676         if(this.active){
22677             cfg.cls += ' active';
22678         }
22679         
22680         if(this.tabId){
22681             cfg.tabId = this.tabId;
22682         }
22683         
22684         
22685         
22686         return cfg;
22687     },
22688     
22689     initEvents:  function()
22690     {
22691         var p = this.parent();
22692         
22693         this.navId = this.navId || p.navId;
22694         
22695         if (typeof(this.navId) != 'undefined') {
22696             // not really needed.. but just in case.. parent should be a NavGroup.
22697             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22698             
22699             tg.register(this);
22700             
22701             var i = tg.tabs.length - 1;
22702             
22703             if(this.active && tg.bullets > 0 && i < tg.bullets){
22704                 tg.setActiveBullet(i);
22705             }
22706         }
22707         
22708         this.el.on('click', this.onClick, this);
22709         
22710         if(Roo.isTouch && this.touchSlide){
22711             this.el.on("touchstart", this.onTouchStart, this);
22712             this.el.on("touchmove", this.onTouchMove, this);
22713             this.el.on("touchend", this.onTouchEnd, this);
22714         }
22715         
22716     },
22717     
22718     onRender : function(ct, position)
22719     {
22720         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22721     },
22722     
22723     setActive : function(state)
22724     {
22725         Roo.log("panel - set active " + this.tabId + "=" + state);
22726         
22727         this.active = state;
22728         if (!state) {
22729             this.el.removeClass('active');
22730             
22731         } else  if (!this.el.hasClass('active')) {
22732             this.el.addClass('active');
22733         }
22734         
22735         this.fireEvent('changed', this, state);
22736     },
22737     
22738     onClick : function(e)
22739     {
22740         e.preventDefault();
22741         
22742         if(!this.href.length){
22743             return;
22744         }
22745         
22746         window.location.href = this.href;
22747     },
22748     
22749     startX : 0,
22750     startY : 0,
22751     endX : 0,
22752     endY : 0,
22753     swiping : false,
22754     
22755     onTouchStart : function(e)
22756     {
22757         this.swiping = false;
22758         
22759         this.startX = e.browserEvent.touches[0].clientX;
22760         this.startY = e.browserEvent.touches[0].clientY;
22761     },
22762     
22763     onTouchMove : function(e)
22764     {
22765         this.swiping = true;
22766         
22767         this.endX = e.browserEvent.touches[0].clientX;
22768         this.endY = e.browserEvent.touches[0].clientY;
22769     },
22770     
22771     onTouchEnd : function(e)
22772     {
22773         if(!this.swiping){
22774             this.onClick(e);
22775             return;
22776         }
22777         
22778         var tabGroup = this.parent();
22779         
22780         if(this.endX > this.startX){ // swiping right
22781             tabGroup.showPanelPrev();
22782             return;
22783         }
22784         
22785         if(this.startX > this.endX){ // swiping left
22786             tabGroup.showPanelNext();
22787             return;
22788         }
22789     }
22790     
22791     
22792 });
22793  
22794
22795  
22796
22797  /*
22798  * - LGPL
22799  *
22800  * DateField
22801  * 
22802  */
22803
22804 /**
22805  * @class Roo.bootstrap.form.DateField
22806  * @extends Roo.bootstrap.form.Input
22807  * Bootstrap DateField class
22808  * @cfg {Number} weekStart default 0
22809  * @cfg {String} viewMode default empty, (months|years)
22810  * @cfg {String} minViewMode default empty, (months|years)
22811  * @cfg {Number} startDate default -Infinity
22812  * @cfg {Number} endDate default Infinity
22813  * @cfg {Boolean} todayHighlight default false
22814  * @cfg {Boolean} todayBtn default false
22815  * @cfg {Boolean} calendarWeeks default false
22816  * @cfg {Object} daysOfWeekDisabled default empty
22817  * @cfg {Boolean} singleMode default false (true | false)
22818  * 
22819  * @cfg {Boolean} keyboardNavigation default true
22820  * @cfg {String} language default en
22821  * 
22822  * @constructor
22823  * Create a new DateField
22824  * @param {Object} config The config object
22825  */
22826
22827 Roo.bootstrap.form.DateField = function(config){
22828     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22829      this.addEvents({
22830             /**
22831              * @event show
22832              * Fires when this field show.
22833              * @param {Roo.bootstrap.form.DateField} this
22834              * @param {Mixed} date The date value
22835              */
22836             show : true,
22837             /**
22838              * @event show
22839              * Fires when this field hide.
22840              * @param {Roo.bootstrap.form.DateField} this
22841              * @param {Mixed} date The date value
22842              */
22843             hide : true,
22844             /**
22845              * @event select
22846              * Fires when select a date.
22847              * @param {Roo.bootstrap.form.DateField} this
22848              * @param {Mixed} date The date value
22849              */
22850             select : true,
22851             /**
22852              * @event beforeselect
22853              * Fires when before select a date.
22854              * @param {Roo.bootstrap.form.DateField} this
22855              * @param {Mixed} date The date value
22856              */
22857             beforeselect : true
22858         });
22859 };
22860
22861 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22862     
22863     /**
22864      * @cfg {String} format
22865      * The default date format string which can be overriden for localization support.  The format must be
22866      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22867      */
22868     format : "m/d/y",
22869     /**
22870      * @cfg {String} altFormats
22871      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22872      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22873      */
22874     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22875     
22876     weekStart : 0,
22877     
22878     viewMode : '',
22879     
22880     minViewMode : '',
22881     
22882     todayHighlight : false,
22883     
22884     todayBtn: false,
22885     
22886     language: 'en',
22887     
22888     keyboardNavigation: true,
22889     
22890     calendarWeeks: false,
22891     
22892     startDate: -Infinity,
22893     
22894     endDate: Infinity,
22895     
22896     daysOfWeekDisabled: [],
22897     
22898     _events: [],
22899     
22900     singleMode : false,
22901     
22902     UTCDate: function()
22903     {
22904         return new Date(Date.UTC.apply(Date, arguments));
22905     },
22906     
22907     UTCToday: function()
22908     {
22909         var today = new Date();
22910         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22911     },
22912     
22913     getDate: function() {
22914             var d = this.getUTCDate();
22915             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22916     },
22917     
22918     getUTCDate: function() {
22919             return this.date;
22920     },
22921     
22922     setDate: function(d) {
22923             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22924     },
22925     
22926     setUTCDate: function(d) {
22927             this.date = d;
22928             this.setValue(this.formatDate(this.date));
22929     },
22930         
22931     onRender: function(ct, position)
22932     {
22933         
22934         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22935         
22936         this.language = this.language || 'en';
22937         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22938         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22939         
22940         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22941         this.format = this.format || 'm/d/y';
22942         this.isInline = false;
22943         this.isInput = true;
22944         this.component = this.el.select('.add-on', true).first() || false;
22945         this.component = (this.component && this.component.length === 0) ? false : this.component;
22946         this.hasInput = this.component && this.inputEl().length;
22947         
22948         if (typeof(this.minViewMode === 'string')) {
22949             switch (this.minViewMode) {
22950                 case 'months':
22951                     this.minViewMode = 1;
22952                     break;
22953                 case 'years':
22954                     this.minViewMode = 2;
22955                     break;
22956                 default:
22957                     this.minViewMode = 0;
22958                     break;
22959             }
22960         }
22961         
22962         if (typeof(this.viewMode === 'string')) {
22963             switch (this.viewMode) {
22964                 case 'months':
22965                     this.viewMode = 1;
22966                     break;
22967                 case 'years':
22968                     this.viewMode = 2;
22969                     break;
22970                 default:
22971                     this.viewMode = 0;
22972                     break;
22973             }
22974         }
22975                 
22976         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22977         
22978 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22979         
22980         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22981         
22982         this.picker().on('mousedown', this.onMousedown, this);
22983         this.picker().on('click', this.onClick, this);
22984         
22985         this.picker().addClass('datepicker-dropdown');
22986         
22987         this.startViewMode = this.viewMode;
22988         
22989         if(this.singleMode){
22990             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22991                 v.setVisibilityMode(Roo.Element.DISPLAY);
22992                 v.hide();
22993             });
22994             
22995             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22996                 v.setStyle('width', '189px');
22997             });
22998         }
22999         
23000         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23001             if(!this.calendarWeeks){
23002                 v.remove();
23003                 return;
23004             }
23005             
23006             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23007             v.attr('colspan', function(i, val){
23008                 return parseInt(val) + 1;
23009             });
23010         });
23011                         
23012         
23013         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23014         
23015         this.setStartDate(this.startDate);
23016         this.setEndDate(this.endDate);
23017         
23018         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23019         
23020         this.fillDow();
23021         this.fillMonths();
23022         this.update();
23023         this.showMode();
23024         
23025         if(this.isInline) {
23026             this.showPopup();
23027         }
23028     },
23029     
23030     picker : function()
23031     {
23032         return this.pickerEl;
23033 //        return this.el.select('.datepicker', true).first();
23034     },
23035     
23036     fillDow: function()
23037     {
23038         var dowCnt = this.weekStart;
23039         
23040         var dow = {
23041             tag: 'tr',
23042             cn: [
23043                 
23044             ]
23045         };
23046         
23047         if(this.calendarWeeks){
23048             dow.cn.push({
23049                 tag: 'th',
23050                 cls: 'cw',
23051                 html: '&nbsp;'
23052             })
23053         }
23054         
23055         while (dowCnt < this.weekStart + 7) {
23056             dow.cn.push({
23057                 tag: 'th',
23058                 cls: 'dow',
23059                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23060             });
23061         }
23062         
23063         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23064     },
23065     
23066     fillMonths: function()
23067     {    
23068         var i = 0;
23069         var months = this.picker().select('>.datepicker-months td', true).first();
23070         
23071         months.dom.innerHTML = '';
23072         
23073         while (i < 12) {
23074             var month = {
23075                 tag: 'span',
23076                 cls: 'month',
23077                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23078             };
23079             
23080             months.createChild(month);
23081         }
23082         
23083     },
23084     
23085     update: function()
23086     {
23087         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;
23088         
23089         if (this.date < this.startDate) {
23090             this.viewDate = new Date(this.startDate);
23091         } else if (this.date > this.endDate) {
23092             this.viewDate = new Date(this.endDate);
23093         } else {
23094             this.viewDate = new Date(this.date);
23095         }
23096         
23097         this.fill();
23098     },
23099     
23100     fill: function() 
23101     {
23102         var d = new Date(this.viewDate),
23103                 year = d.getUTCFullYear(),
23104                 month = d.getUTCMonth(),
23105                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23106                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23107                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23108                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23109                 currentDate = this.date && this.date.valueOf(),
23110                 today = this.UTCToday();
23111         
23112         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23113         
23114 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23115         
23116 //        this.picker.select('>tfoot th.today').
23117 //                                              .text(dates[this.language].today)
23118 //                                              .toggle(this.todayBtn !== false);
23119     
23120         this.updateNavArrows();
23121         this.fillMonths();
23122                                                 
23123         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23124         
23125         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23126          
23127         prevMonth.setUTCDate(day);
23128         
23129         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23130         
23131         var nextMonth = new Date(prevMonth);
23132         
23133         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23134         
23135         nextMonth = nextMonth.valueOf();
23136         
23137         var fillMonths = false;
23138         
23139         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23140         
23141         while(prevMonth.valueOf() <= nextMonth) {
23142             var clsName = '';
23143             
23144             if (prevMonth.getUTCDay() === this.weekStart) {
23145                 if(fillMonths){
23146                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23147                 }
23148                     
23149                 fillMonths = {
23150                     tag: 'tr',
23151                     cn: []
23152                 };
23153                 
23154                 if(this.calendarWeeks){
23155                     // ISO 8601: First week contains first thursday.
23156                     // ISO also states week starts on Monday, but we can be more abstract here.
23157                     var
23158                     // Start of current week: based on weekstart/current date
23159                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23160                     // Thursday of this week
23161                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23162                     // First Thursday of year, year from thursday
23163                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23164                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23165                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23166                     
23167                     fillMonths.cn.push({
23168                         tag: 'td',
23169                         cls: 'cw',
23170                         html: calWeek
23171                     });
23172                 }
23173             }
23174             
23175             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23176                 clsName += ' old';
23177             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23178                 clsName += ' new';
23179             }
23180             if (this.todayHighlight &&
23181                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23182                 prevMonth.getUTCMonth() == today.getMonth() &&
23183                 prevMonth.getUTCDate() == today.getDate()) {
23184                 clsName += ' today';
23185             }
23186             
23187             if (currentDate && prevMonth.valueOf() === currentDate) {
23188                 clsName += ' active';
23189             }
23190             
23191             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23192                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23193                     clsName += ' disabled';
23194             }
23195             
23196             fillMonths.cn.push({
23197                 tag: 'td',
23198                 cls: 'day ' + clsName,
23199                 html: prevMonth.getDate()
23200             });
23201             
23202             prevMonth.setDate(prevMonth.getDate()+1);
23203         }
23204           
23205         var currentYear = this.date && this.date.getUTCFullYear();
23206         var currentMonth = this.date && this.date.getUTCMonth();
23207         
23208         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23209         
23210         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23211             v.removeClass('active');
23212             
23213             if(currentYear === year && k === currentMonth){
23214                 v.addClass('active');
23215             }
23216             
23217             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23218                 v.addClass('disabled');
23219             }
23220             
23221         });
23222         
23223         
23224         year = parseInt(year/10, 10) * 10;
23225         
23226         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23227         
23228         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23229         
23230         year -= 1;
23231         for (var i = -1; i < 11; i++) {
23232             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23233                 tag: 'span',
23234                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23235                 html: year
23236             });
23237             
23238             year += 1;
23239         }
23240     },
23241     
23242     showMode: function(dir) 
23243     {
23244         if (dir) {
23245             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23246         }
23247         
23248         Roo.each(this.picker().select('>div',true).elements, function(v){
23249             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23250             v.hide();
23251         });
23252         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23253     },
23254     
23255     place: function()
23256     {
23257         if(this.isInline) {
23258             return;
23259         }
23260         
23261         this.picker().removeClass(['bottom', 'top']);
23262         
23263         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23264             /*
23265              * place to the top of element!
23266              *
23267              */
23268             
23269             this.picker().addClass('top');
23270             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23271             
23272             return;
23273         }
23274         
23275         this.picker().addClass('bottom');
23276         
23277         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23278     },
23279     
23280     parseDate : function(value)
23281     {
23282         if(!value || value instanceof Date){
23283             return value;
23284         }
23285         var v = Date.parseDate(value, this.format);
23286         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23287             v = Date.parseDate(value, 'Y-m-d');
23288         }
23289         if(!v && this.altFormats){
23290             if(!this.altFormatsArray){
23291                 this.altFormatsArray = this.altFormats.split("|");
23292             }
23293             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23294                 v = Date.parseDate(value, this.altFormatsArray[i]);
23295             }
23296         }
23297         return v;
23298     },
23299     
23300     formatDate : function(date, fmt)
23301     {   
23302         return (!date || !(date instanceof Date)) ?
23303         date : date.dateFormat(fmt || this.format);
23304     },
23305     
23306     onFocus : function()
23307     {
23308         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23309         this.showPopup();
23310     },
23311     
23312     onBlur : function()
23313     {
23314         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23315         
23316         var d = this.inputEl().getValue();
23317         
23318         this.setValue(d);
23319                 
23320         this.hidePopup();
23321     },
23322     
23323     showPopup : function()
23324     {
23325         this.picker().show();
23326         this.update();
23327         this.place();
23328         
23329         this.fireEvent('showpopup', this, this.date);
23330     },
23331     
23332     hidePopup : function()
23333     {
23334         if(this.isInline) {
23335             return;
23336         }
23337         this.picker().hide();
23338         this.viewMode = this.startViewMode;
23339         this.showMode();
23340         
23341         this.fireEvent('hidepopup', this, this.date);
23342         
23343     },
23344     
23345     onMousedown: function(e)
23346     {
23347         e.stopPropagation();
23348         e.preventDefault();
23349     },
23350     
23351     keyup: function(e)
23352     {
23353         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23354         this.update();
23355     },
23356
23357     setValue: function(v)
23358     {
23359         if(this.fireEvent('beforeselect', this, v) !== false){
23360             var d = new Date(this.parseDate(v) ).clearTime();
23361         
23362             if(isNaN(d.getTime())){
23363                 this.date = this.viewDate = '';
23364                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23365                 return;
23366             }
23367
23368             v = this.formatDate(d);
23369
23370             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23371
23372             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23373
23374             this.update();
23375
23376             this.fireEvent('select', this, this.date);
23377         }
23378     },
23379     
23380     getValue: function()
23381     {
23382         return this.formatDate(this.date);
23383     },
23384     
23385     fireKey: function(e)
23386     {
23387         if (!this.picker().isVisible()){
23388             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23389                 this.showPopup();
23390             }
23391             return;
23392         }
23393         
23394         var dateChanged = false,
23395         dir, day, month,
23396         newDate, newViewDate;
23397         
23398         switch(e.keyCode){
23399             case 27: // escape
23400                 this.hidePopup();
23401                 e.preventDefault();
23402                 break;
23403             case 37: // left
23404             case 39: // right
23405                 if (!this.keyboardNavigation) {
23406                     break;
23407                 }
23408                 dir = e.keyCode == 37 ? -1 : 1;
23409                 
23410                 if (e.ctrlKey){
23411                     newDate = this.moveYear(this.date, dir);
23412                     newViewDate = this.moveYear(this.viewDate, dir);
23413                 } else if (e.shiftKey){
23414                     newDate = this.moveMonth(this.date, dir);
23415                     newViewDate = this.moveMonth(this.viewDate, dir);
23416                 } else {
23417                     newDate = new Date(this.date);
23418                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23419                     newViewDate = new Date(this.viewDate);
23420                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23421                 }
23422                 if (this.dateWithinRange(newDate)){
23423                     this.date = newDate;
23424                     this.viewDate = newViewDate;
23425                     this.setValue(this.formatDate(this.date));
23426 //                    this.update();
23427                     e.preventDefault();
23428                     dateChanged = true;
23429                 }
23430                 break;
23431             case 38: // up
23432             case 40: // down
23433                 if (!this.keyboardNavigation) {
23434                     break;
23435                 }
23436                 dir = e.keyCode == 38 ? -1 : 1;
23437                 if (e.ctrlKey){
23438                     newDate = this.moveYear(this.date, dir);
23439                     newViewDate = this.moveYear(this.viewDate, dir);
23440                 } else if (e.shiftKey){
23441                     newDate = this.moveMonth(this.date, dir);
23442                     newViewDate = this.moveMonth(this.viewDate, dir);
23443                 } else {
23444                     newDate = new Date(this.date);
23445                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23446                     newViewDate = new Date(this.viewDate);
23447                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23448                 }
23449                 if (this.dateWithinRange(newDate)){
23450                     this.date = newDate;
23451                     this.viewDate = newViewDate;
23452                     this.setValue(this.formatDate(this.date));
23453 //                    this.update();
23454                     e.preventDefault();
23455                     dateChanged = true;
23456                 }
23457                 break;
23458             case 13: // enter
23459                 this.setValue(this.formatDate(this.date));
23460                 this.hidePopup();
23461                 e.preventDefault();
23462                 break;
23463             case 9: // tab
23464                 this.setValue(this.formatDate(this.date));
23465                 this.hidePopup();
23466                 break;
23467             case 16: // shift
23468             case 17: // ctrl
23469             case 18: // alt
23470                 break;
23471             default :
23472                 this.hidePopup();
23473                 
23474         }
23475     },
23476     
23477     
23478     onClick: function(e) 
23479     {
23480         e.stopPropagation();
23481         e.preventDefault();
23482         
23483         var target = e.getTarget();
23484         
23485         if(target.nodeName.toLowerCase() === 'i'){
23486             target = Roo.get(target).dom.parentNode;
23487         }
23488         
23489         var nodeName = target.nodeName;
23490         var className = target.className;
23491         var html = target.innerHTML;
23492         //Roo.log(nodeName);
23493         
23494         switch(nodeName.toLowerCase()) {
23495             case 'th':
23496                 switch(className) {
23497                     case 'switch':
23498                         this.showMode(1);
23499                         break;
23500                     case 'prev':
23501                     case 'next':
23502                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23503                         switch(this.viewMode){
23504                                 case 0:
23505                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23506                                         break;
23507                                 case 1:
23508                                 case 2:
23509                                         this.viewDate = this.moveYear(this.viewDate, dir);
23510                                         break;
23511                         }
23512                         this.fill();
23513                         break;
23514                     case 'today':
23515                         var date = new Date();
23516                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23517 //                        this.fill()
23518                         this.setValue(this.formatDate(this.date));
23519                         
23520                         this.hidePopup();
23521                         break;
23522                 }
23523                 break;
23524             case 'span':
23525                 if (className.indexOf('disabled') < 0) {
23526                 if (!this.viewDate) {
23527                     this.viewDate = new Date();
23528                 }
23529                 this.viewDate.setUTCDate(1);
23530                     if (className.indexOf('month') > -1) {
23531                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23532                     } else {
23533                         var year = parseInt(html, 10) || 0;
23534                         this.viewDate.setUTCFullYear(year);
23535                         
23536                     }
23537                     
23538                     if(this.singleMode){
23539                         this.setValue(this.formatDate(this.viewDate));
23540                         this.hidePopup();
23541                         return;
23542                     }
23543                     
23544                     this.showMode(-1);
23545                     this.fill();
23546                 }
23547                 break;
23548                 
23549             case 'td':
23550                 //Roo.log(className);
23551                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23552                     var day = parseInt(html, 10) || 1;
23553                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23554                         month = (this.viewDate || new Date()).getUTCMonth();
23555
23556                     if (className.indexOf('old') > -1) {
23557                         if(month === 0 ){
23558                             month = 11;
23559                             year -= 1;
23560                         }else{
23561                             month -= 1;
23562                         }
23563                     } else if (className.indexOf('new') > -1) {
23564                         if (month == 11) {
23565                             month = 0;
23566                             year += 1;
23567                         } else {
23568                             month += 1;
23569                         }
23570                     }
23571                     //Roo.log([year,month,day]);
23572                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23573                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23574 //                    this.fill();
23575                     //Roo.log(this.formatDate(this.date));
23576                     this.setValue(this.formatDate(this.date));
23577                     this.hidePopup();
23578                 }
23579                 break;
23580         }
23581     },
23582     
23583     setStartDate: function(startDate)
23584     {
23585         this.startDate = startDate || -Infinity;
23586         if (this.startDate !== -Infinity) {
23587             this.startDate = this.parseDate(this.startDate);
23588         }
23589         this.update();
23590         this.updateNavArrows();
23591     },
23592
23593     setEndDate: function(endDate)
23594     {
23595         this.endDate = endDate || Infinity;
23596         if (this.endDate !== Infinity) {
23597             this.endDate = this.parseDate(this.endDate);
23598         }
23599         this.update();
23600         this.updateNavArrows();
23601     },
23602     
23603     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23604     {
23605         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23606         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23607             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23608         }
23609         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23610             return parseInt(d, 10);
23611         });
23612         this.update();
23613         this.updateNavArrows();
23614     },
23615     
23616     updateNavArrows: function() 
23617     {
23618         if(this.singleMode){
23619             return;
23620         }
23621         
23622         var d = new Date(this.viewDate),
23623         year = d.getUTCFullYear(),
23624         month = d.getUTCMonth();
23625         
23626         Roo.each(this.picker().select('.prev', true).elements, function(v){
23627             v.show();
23628             switch (this.viewMode) {
23629                 case 0:
23630
23631                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23632                         v.hide();
23633                     }
23634                     break;
23635                 case 1:
23636                 case 2:
23637                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23638                         v.hide();
23639                     }
23640                     break;
23641             }
23642         });
23643         
23644         Roo.each(this.picker().select('.next', true).elements, function(v){
23645             v.show();
23646             switch (this.viewMode) {
23647                 case 0:
23648
23649                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23650                         v.hide();
23651                     }
23652                     break;
23653                 case 1:
23654                 case 2:
23655                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23656                         v.hide();
23657                     }
23658                     break;
23659             }
23660         })
23661     },
23662     
23663     moveMonth: function(date, dir)
23664     {
23665         if (!dir) {
23666             return date;
23667         }
23668         var new_date = new Date(date.valueOf()),
23669         day = new_date.getUTCDate(),
23670         month = new_date.getUTCMonth(),
23671         mag = Math.abs(dir),
23672         new_month, test;
23673         dir = dir > 0 ? 1 : -1;
23674         if (mag == 1){
23675             test = dir == -1
23676             // If going back one month, make sure month is not current month
23677             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23678             ? function(){
23679                 return new_date.getUTCMonth() == month;
23680             }
23681             // If going forward one month, make sure month is as expected
23682             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23683             : function(){
23684                 return new_date.getUTCMonth() != new_month;
23685             };
23686             new_month = month + dir;
23687             new_date.setUTCMonth(new_month);
23688             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23689             if (new_month < 0 || new_month > 11) {
23690                 new_month = (new_month + 12) % 12;
23691             }
23692         } else {
23693             // For magnitudes >1, move one month at a time...
23694             for (var i=0; i<mag; i++) {
23695                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23696                 new_date = this.moveMonth(new_date, dir);
23697             }
23698             // ...then reset the day, keeping it in the new month
23699             new_month = new_date.getUTCMonth();
23700             new_date.setUTCDate(day);
23701             test = function(){
23702                 return new_month != new_date.getUTCMonth();
23703             };
23704         }
23705         // Common date-resetting loop -- if date is beyond end of month, make it
23706         // end of month
23707         while (test()){
23708             new_date.setUTCDate(--day);
23709             new_date.setUTCMonth(new_month);
23710         }
23711         return new_date;
23712     },
23713
23714     moveYear: function(date, dir)
23715     {
23716         return this.moveMonth(date, dir*12);
23717     },
23718
23719     dateWithinRange: function(date)
23720     {
23721         return date >= this.startDate && date <= this.endDate;
23722     },
23723
23724     
23725     remove: function() 
23726     {
23727         this.picker().remove();
23728     },
23729     
23730     validateValue : function(value)
23731     {
23732         if(this.getVisibilityEl().hasClass('hidden')){
23733             return true;
23734         }
23735         
23736         if(value.length < 1)  {
23737             if(this.allowBlank){
23738                 return true;
23739             }
23740             return false;
23741         }
23742         
23743         if(value.length < this.minLength){
23744             return false;
23745         }
23746         if(value.length > this.maxLength){
23747             return false;
23748         }
23749         if(this.vtype){
23750             var vt = Roo.form.VTypes;
23751             if(!vt[this.vtype](value, this)){
23752                 return false;
23753             }
23754         }
23755         if(typeof this.validator == "function"){
23756             var msg = this.validator(value);
23757             if(msg !== true){
23758                 return false;
23759             }
23760         }
23761         
23762         if(this.regex && !this.regex.test(value)){
23763             return false;
23764         }
23765         
23766         if(typeof(this.parseDate(value)) == 'undefined'){
23767             return false;
23768         }
23769         
23770         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23771             return false;
23772         }      
23773         
23774         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23775             return false;
23776         } 
23777         
23778         
23779         return true;
23780     },
23781     
23782     reset : function()
23783     {
23784         this.date = this.viewDate = '';
23785         
23786         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23787     }
23788    
23789 });
23790
23791 Roo.apply(Roo.bootstrap.form.DateField,  {
23792     
23793     head : {
23794         tag: 'thead',
23795         cn: [
23796         {
23797             tag: 'tr',
23798             cn: [
23799             {
23800                 tag: 'th',
23801                 cls: 'prev',
23802                 html: '<i class="fa fa-arrow-left"/>'
23803             },
23804             {
23805                 tag: 'th',
23806                 cls: 'switch',
23807                 colspan: '5'
23808             },
23809             {
23810                 tag: 'th',
23811                 cls: 'next',
23812                 html: '<i class="fa fa-arrow-right"/>'
23813             }
23814
23815             ]
23816         }
23817         ]
23818     },
23819     
23820     content : {
23821         tag: 'tbody',
23822         cn: [
23823         {
23824             tag: 'tr',
23825             cn: [
23826             {
23827                 tag: 'td',
23828                 colspan: '7'
23829             }
23830             ]
23831         }
23832         ]
23833     },
23834     
23835     footer : {
23836         tag: 'tfoot',
23837         cn: [
23838         {
23839             tag: 'tr',
23840             cn: [
23841             {
23842                 tag: 'th',
23843                 colspan: '7',
23844                 cls: 'today'
23845             }
23846                     
23847             ]
23848         }
23849         ]
23850     },
23851     
23852     dates:{
23853         en: {
23854             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23855             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23856             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23857             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23858             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23859             today: "Today"
23860         }
23861     },
23862     
23863     modes: [
23864     {
23865         clsName: 'days',
23866         navFnc: 'Month',
23867         navStep: 1
23868     },
23869     {
23870         clsName: 'months',
23871         navFnc: 'FullYear',
23872         navStep: 1
23873     },
23874     {
23875         clsName: 'years',
23876         navFnc: 'FullYear',
23877         navStep: 10
23878     }]
23879 });
23880
23881 Roo.apply(Roo.bootstrap.form.DateField,  {
23882   
23883     template : {
23884         tag: 'div',
23885         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23886         cn: [
23887         {
23888             tag: 'div',
23889             cls: 'datepicker-days',
23890             cn: [
23891             {
23892                 tag: 'table',
23893                 cls: 'table-condensed',
23894                 cn:[
23895                 Roo.bootstrap.form.DateField.head,
23896                 {
23897                     tag: 'tbody'
23898                 },
23899                 Roo.bootstrap.form.DateField.footer
23900                 ]
23901             }
23902             ]
23903         },
23904         {
23905             tag: 'div',
23906             cls: 'datepicker-months',
23907             cn: [
23908             {
23909                 tag: 'table',
23910                 cls: 'table-condensed',
23911                 cn:[
23912                 Roo.bootstrap.form.DateField.head,
23913                 Roo.bootstrap.form.DateField.content,
23914                 Roo.bootstrap.form.DateField.footer
23915                 ]
23916             }
23917             ]
23918         },
23919         {
23920             tag: 'div',
23921             cls: 'datepicker-years',
23922             cn: [
23923             {
23924                 tag: 'table',
23925                 cls: 'table-condensed',
23926                 cn:[
23927                 Roo.bootstrap.form.DateField.head,
23928                 Roo.bootstrap.form.DateField.content,
23929                 Roo.bootstrap.form.DateField.footer
23930                 ]
23931             }
23932             ]
23933         }
23934         ]
23935     }
23936 });
23937
23938  
23939
23940  /*
23941  * - LGPL
23942  *
23943  * TimeField
23944  * 
23945  */
23946
23947 /**
23948  * @class Roo.bootstrap.form.TimeField
23949  * @extends Roo.bootstrap.form.Input
23950  * Bootstrap DateField class
23951  * 
23952  * 
23953  * @constructor
23954  * Create a new TimeField
23955  * @param {Object} config The config object
23956  */
23957
23958 Roo.bootstrap.form.TimeField = function(config){
23959     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23960     this.addEvents({
23961             /**
23962              * @event show
23963              * Fires when this field show.
23964              * @param {Roo.bootstrap.form.DateField} thisthis
23965              * @param {Mixed} date The date value
23966              */
23967             show : true,
23968             /**
23969              * @event show
23970              * Fires when this field hide.
23971              * @param {Roo.bootstrap.form.DateField} this
23972              * @param {Mixed} date The date value
23973              */
23974             hide : true,
23975             /**
23976              * @event select
23977              * Fires when select a date.
23978              * @param {Roo.bootstrap.form.DateField} this
23979              * @param {Mixed} date The date value
23980              */
23981             select : true
23982         });
23983 };
23984
23985 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23986     
23987     /**
23988      * @cfg {String} format
23989      * The default time format string which can be overriden for localization support.  The format must be
23990      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23991      */
23992     format : "H:i",
23993
23994     getAutoCreate : function()
23995     {
23996         this.after = '<i class="fa far fa-clock"></i>';
23997         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23998         
23999          
24000     },
24001     onRender: function(ct, position)
24002     {
24003         
24004         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24005                 
24006         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24007         
24008         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24009         
24010         this.pop = this.picker().select('>.datepicker-time',true).first();
24011         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24012         
24013         this.picker().on('mousedown', this.onMousedown, this);
24014         this.picker().on('click', this.onClick, this);
24015         
24016         this.picker().addClass('datepicker-dropdown');
24017     
24018         this.fillTime();
24019         this.update();
24020             
24021         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24022         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24023         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24024         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24025         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24026         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24027
24028     },
24029     
24030     fireKey: function(e){
24031         if (!this.picker().isVisible()){
24032             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24033                 this.show();
24034             }
24035             return;
24036         }
24037
24038         e.preventDefault();
24039         
24040         switch(e.keyCode){
24041             case 27: // escape
24042                 this.hide();
24043                 break;
24044             case 37: // left
24045             case 39: // right
24046                 this.onTogglePeriod();
24047                 break;
24048             case 38: // up
24049                 this.onIncrementMinutes();
24050                 break;
24051             case 40: // down
24052                 this.onDecrementMinutes();
24053                 break;
24054             case 13: // enter
24055             case 9: // tab
24056                 this.setTime();
24057                 break;
24058         }
24059     },
24060     
24061     onClick: function(e) {
24062         e.stopPropagation();
24063         e.preventDefault();
24064     },
24065     
24066     picker : function()
24067     {
24068         return this.pickerEl;
24069     },
24070     
24071     fillTime: function()
24072     {    
24073         var time = this.pop.select('tbody', true).first();
24074         
24075         time.dom.innerHTML = '';
24076         
24077         time.createChild({
24078             tag: 'tr',
24079             cn: [
24080                 {
24081                     tag: 'td',
24082                     cn: [
24083                         {
24084                             tag: 'a',
24085                             href: '#',
24086                             cls: 'btn',
24087                             cn: [
24088                                 {
24089                                     tag: 'i',
24090                                     cls: 'hours-up fa fas fa-chevron-up'
24091                                 }
24092                             ]
24093                         } 
24094                     ]
24095                 },
24096                 {
24097                     tag: 'td',
24098                     cls: 'separator'
24099                 },
24100                 {
24101                     tag: 'td',
24102                     cn: [
24103                         {
24104                             tag: 'a',
24105                             href: '#',
24106                             cls: 'btn',
24107                             cn: [
24108                                 {
24109                                     tag: 'i',
24110                                     cls: 'minutes-up fa fas fa-chevron-up'
24111                                 }
24112                             ]
24113                         }
24114                     ]
24115                 },
24116                 {
24117                     tag: 'td',
24118                     cls: 'separator'
24119                 }
24120             ]
24121         });
24122         
24123         time.createChild({
24124             tag: 'tr',
24125             cn: [
24126                 {
24127                     tag: 'td',
24128                     cn: [
24129                         {
24130                             tag: 'span',
24131                             cls: 'timepicker-hour',
24132                             html: '00'
24133                         }  
24134                     ]
24135                 },
24136                 {
24137                     tag: 'td',
24138                     cls: 'separator',
24139                     html: ':'
24140                 },
24141                 {
24142                     tag: 'td',
24143                     cn: [
24144                         {
24145                             tag: 'span',
24146                             cls: 'timepicker-minute',
24147                             html: '00'
24148                         }  
24149                     ]
24150                 },
24151                 {
24152                     tag: 'td',
24153                     cls: 'separator'
24154                 },
24155                 {
24156                     tag: 'td',
24157                     cn: [
24158                         {
24159                             tag: 'button',
24160                             type: 'button',
24161                             cls: 'btn btn-primary period',
24162                             html: 'AM'
24163                             
24164                         }
24165                     ]
24166                 }
24167             ]
24168         });
24169         
24170         time.createChild({
24171             tag: 'tr',
24172             cn: [
24173                 {
24174                     tag: 'td',
24175                     cn: [
24176                         {
24177                             tag: 'a',
24178                             href: '#',
24179                             cls: 'btn',
24180                             cn: [
24181                                 {
24182                                     tag: 'span',
24183                                     cls: 'hours-down fa fas fa-chevron-down'
24184                                 }
24185                             ]
24186                         }
24187                     ]
24188                 },
24189                 {
24190                     tag: 'td',
24191                     cls: 'separator'
24192                 },
24193                 {
24194                     tag: 'td',
24195                     cn: [
24196                         {
24197                             tag: 'a',
24198                             href: '#',
24199                             cls: 'btn',
24200                             cn: [
24201                                 {
24202                                     tag: 'span',
24203                                     cls: 'minutes-down fa fas fa-chevron-down'
24204                                 }
24205                             ]
24206                         }
24207                     ]
24208                 },
24209                 {
24210                     tag: 'td',
24211                     cls: 'separator'
24212                 }
24213             ]
24214         });
24215         
24216     },
24217     
24218     update: function()
24219     {
24220         
24221         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24222         
24223         this.fill();
24224     },
24225     
24226     fill: function() 
24227     {
24228         var hours = this.time.getHours();
24229         var minutes = this.time.getMinutes();
24230         var period = 'AM';
24231         
24232         if(hours > 11){
24233             period = 'PM';
24234         }
24235         
24236         if(hours == 0){
24237             hours = 12;
24238         }
24239         
24240         
24241         if(hours > 12){
24242             hours = hours - 12;
24243         }
24244         
24245         if(hours < 10){
24246             hours = '0' + hours;
24247         }
24248         
24249         if(minutes < 10){
24250             minutes = '0' + minutes;
24251         }
24252         
24253         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24254         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24255         this.pop.select('button', true).first().dom.innerHTML = period;
24256         
24257     },
24258     
24259     place: function()
24260     {   
24261         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24262         
24263         var cls = ['bottom'];
24264         
24265         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24266             cls.pop();
24267             cls.push('top');
24268         }
24269         
24270         cls.push('right');
24271         
24272         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24273             cls.pop();
24274             cls.push('left');
24275         }
24276         //this.picker().setXY(20000,20000);
24277         this.picker().addClass(cls.join('-'));
24278         
24279         var _this = this;
24280         
24281         Roo.each(cls, function(c){
24282             if(c == 'bottom'){
24283                 (function() {
24284                  //  
24285                 }).defer(200);
24286                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24287                 //_this.picker().setTop(_this.inputEl().getHeight());
24288                 return;
24289             }
24290             if(c == 'top'){
24291                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24292                 
24293                 //_this.picker().setTop(0 - _this.picker().getHeight());
24294                 return;
24295             }
24296             /*
24297             if(c == 'left'){
24298                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24299                 return;
24300             }
24301             if(c == 'right'){
24302                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24303                 return;
24304             }
24305             */
24306         });
24307         
24308     },
24309   
24310     onFocus : function()
24311     {
24312         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24313         this.show();
24314     },
24315     
24316     onBlur : function()
24317     {
24318         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24319         this.hide();
24320     },
24321     
24322     show : function()
24323     {
24324         this.picker().show();
24325         this.pop.show();
24326         this.update();
24327         this.place();
24328         
24329         this.fireEvent('show', this, this.date);
24330     },
24331     
24332     hide : function()
24333     {
24334         this.picker().hide();
24335         this.pop.hide();
24336         
24337         this.fireEvent('hide', this, this.date);
24338     },
24339     
24340     setTime : function()
24341     {
24342         this.hide();
24343         this.setValue(this.time.format(this.format));
24344         
24345         this.fireEvent('select', this, this.date);
24346         
24347         
24348     },
24349     
24350     onMousedown: function(e){
24351         e.stopPropagation();
24352         e.preventDefault();
24353     },
24354     
24355     onIncrementHours: function()
24356     {
24357         Roo.log('onIncrementHours');
24358         this.time = this.time.add(Date.HOUR, 1);
24359         this.update();
24360         
24361     },
24362     
24363     onDecrementHours: function()
24364     {
24365         Roo.log('onDecrementHours');
24366         this.time = this.time.add(Date.HOUR, -1);
24367         this.update();
24368     },
24369     
24370     onIncrementMinutes: function()
24371     {
24372         Roo.log('onIncrementMinutes');
24373         this.time = this.time.add(Date.MINUTE, 1);
24374         this.update();
24375     },
24376     
24377     onDecrementMinutes: function()
24378     {
24379         Roo.log('onDecrementMinutes');
24380         this.time = this.time.add(Date.MINUTE, -1);
24381         this.update();
24382     },
24383     
24384     onTogglePeriod: function()
24385     {
24386         Roo.log('onTogglePeriod');
24387         this.time = this.time.add(Date.HOUR, 12);
24388         this.update();
24389     }
24390     
24391    
24392 });
24393  
24394
24395 Roo.apply(Roo.bootstrap.form.TimeField,  {
24396   
24397     template : {
24398         tag: 'div',
24399         cls: 'datepicker dropdown-menu',
24400         cn: [
24401             {
24402                 tag: 'div',
24403                 cls: 'datepicker-time',
24404                 cn: [
24405                 {
24406                     tag: 'table',
24407                     cls: 'table-condensed',
24408                     cn:[
24409                         {
24410                             tag: 'tbody',
24411                             cn: [
24412                                 {
24413                                     tag: 'tr',
24414                                     cn: [
24415                                     {
24416                                         tag: 'td',
24417                                         colspan: '7'
24418                                     }
24419                                     ]
24420                                 }
24421                             ]
24422                         },
24423                         {
24424                             tag: 'tfoot',
24425                             cn: [
24426                                 {
24427                                     tag: 'tr',
24428                                     cn: [
24429                                     {
24430                                         tag: 'th',
24431                                         colspan: '7',
24432                                         cls: '',
24433                                         cn: [
24434                                             {
24435                                                 tag: 'button',
24436                                                 cls: 'btn btn-info ok',
24437                                                 html: 'OK'
24438                                             }
24439                                         ]
24440                                     }
24441                     
24442                                     ]
24443                                 }
24444                             ]
24445                         }
24446                     ]
24447                 }
24448                 ]
24449             }
24450         ]
24451     }
24452 });
24453
24454  
24455
24456  /*
24457  * - LGPL
24458  *
24459  * MonthField
24460  * 
24461  */
24462
24463 /**
24464  * @class Roo.bootstrap.form.MonthField
24465  * @extends Roo.bootstrap.form.Input
24466  * Bootstrap MonthField class
24467  * 
24468  * @cfg {String} language default en
24469  * 
24470  * @constructor
24471  * Create a new MonthField
24472  * @param {Object} config The config object
24473  */
24474
24475 Roo.bootstrap.form.MonthField = function(config){
24476     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24477     
24478     this.addEvents({
24479         /**
24480          * @event show
24481          * Fires when this field show.
24482          * @param {Roo.bootstrap.form.MonthField} this
24483          * @param {Mixed} date The date value
24484          */
24485         show : true,
24486         /**
24487          * @event show
24488          * Fires when this field hide.
24489          * @param {Roo.bootstrap.form.MonthField} this
24490          * @param {Mixed} date The date value
24491          */
24492         hide : true,
24493         /**
24494          * @event select
24495          * Fires when select a date.
24496          * @param {Roo.bootstrap.form.MonthField} this
24497          * @param {String} oldvalue The old value
24498          * @param {String} newvalue The new value
24499          */
24500         select : true
24501     });
24502 };
24503
24504 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24505     
24506     onRender: function(ct, position)
24507     {
24508         
24509         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24510         
24511         this.language = this.language || 'en';
24512         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24513         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24514         
24515         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24516         this.isInline = false;
24517         this.isInput = true;
24518         this.component = this.el.select('.add-on', true).first() || false;
24519         this.component = (this.component && this.component.length === 0) ? false : this.component;
24520         this.hasInput = this.component && this.inputEL().length;
24521         
24522         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24523         
24524         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24525         
24526         this.picker().on('mousedown', this.onMousedown, this);
24527         this.picker().on('click', this.onClick, this);
24528         
24529         this.picker().addClass('datepicker-dropdown');
24530         
24531         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24532             v.setStyle('width', '189px');
24533         });
24534         
24535         this.fillMonths();
24536         
24537         this.update();
24538         
24539         if(this.isInline) {
24540             this.show();
24541         }
24542         
24543     },
24544     
24545     setValue: function(v, suppressEvent)
24546     {   
24547         var o = this.getValue();
24548         
24549         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24550         
24551         this.update();
24552
24553         if(suppressEvent !== true){
24554             this.fireEvent('select', this, o, v);
24555         }
24556         
24557     },
24558     
24559     getValue: function()
24560     {
24561         return this.value;
24562     },
24563     
24564     onClick: function(e) 
24565     {
24566         e.stopPropagation();
24567         e.preventDefault();
24568         
24569         var target = e.getTarget();
24570         
24571         if(target.nodeName.toLowerCase() === 'i'){
24572             target = Roo.get(target).dom.parentNode;
24573         }
24574         
24575         var nodeName = target.nodeName;
24576         var className = target.className;
24577         var html = target.innerHTML;
24578         
24579         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24580             return;
24581         }
24582         
24583         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24584         
24585         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24586         
24587         this.hide();
24588                         
24589     },
24590     
24591     picker : function()
24592     {
24593         return this.pickerEl;
24594     },
24595     
24596     fillMonths: function()
24597     {    
24598         var i = 0;
24599         var months = this.picker().select('>.datepicker-months td', true).first();
24600         
24601         months.dom.innerHTML = '';
24602         
24603         while (i < 12) {
24604             var month = {
24605                 tag: 'span',
24606                 cls: 'month',
24607                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24608             };
24609             
24610             months.createChild(month);
24611         }
24612         
24613     },
24614     
24615     update: function()
24616     {
24617         var _this = this;
24618         
24619         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24620             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24621         }
24622         
24623         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24624             e.removeClass('active');
24625             
24626             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24627                 e.addClass('active');
24628             }
24629         })
24630     },
24631     
24632     place: function()
24633     {
24634         if(this.isInline) {
24635             return;
24636         }
24637         
24638         this.picker().removeClass(['bottom', 'top']);
24639         
24640         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24641             /*
24642              * place to the top of element!
24643              *
24644              */
24645             
24646             this.picker().addClass('top');
24647             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24648             
24649             return;
24650         }
24651         
24652         this.picker().addClass('bottom');
24653         
24654         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24655     },
24656     
24657     onFocus : function()
24658     {
24659         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24660         this.show();
24661     },
24662     
24663     onBlur : function()
24664     {
24665         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24666         
24667         var d = this.inputEl().getValue();
24668         
24669         this.setValue(d);
24670                 
24671         this.hide();
24672     },
24673     
24674     show : function()
24675     {
24676         this.picker().show();
24677         this.picker().select('>.datepicker-months', true).first().show();
24678         this.update();
24679         this.place();
24680         
24681         this.fireEvent('show', this, this.date);
24682     },
24683     
24684     hide : function()
24685     {
24686         if(this.isInline) {
24687             return;
24688         }
24689         this.picker().hide();
24690         this.fireEvent('hide', this, this.date);
24691         
24692     },
24693     
24694     onMousedown: function(e)
24695     {
24696         e.stopPropagation();
24697         e.preventDefault();
24698     },
24699     
24700     keyup: function(e)
24701     {
24702         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24703         this.update();
24704     },
24705
24706     fireKey: function(e)
24707     {
24708         if (!this.picker().isVisible()){
24709             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24710                 this.show();
24711             }
24712             return;
24713         }
24714         
24715         var dir;
24716         
24717         switch(e.keyCode){
24718             case 27: // escape
24719                 this.hide();
24720                 e.preventDefault();
24721                 break;
24722             case 37: // left
24723             case 39: // right
24724                 dir = e.keyCode == 37 ? -1 : 1;
24725                 
24726                 this.vIndex = this.vIndex + dir;
24727                 
24728                 if(this.vIndex < 0){
24729                     this.vIndex = 0;
24730                 }
24731                 
24732                 if(this.vIndex > 11){
24733                     this.vIndex = 11;
24734                 }
24735                 
24736                 if(isNaN(this.vIndex)){
24737                     this.vIndex = 0;
24738                 }
24739                 
24740                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24741                 
24742                 break;
24743             case 38: // up
24744             case 40: // down
24745                 
24746                 dir = e.keyCode == 38 ? -1 : 1;
24747                 
24748                 this.vIndex = this.vIndex + dir * 4;
24749                 
24750                 if(this.vIndex < 0){
24751                     this.vIndex = 0;
24752                 }
24753                 
24754                 if(this.vIndex > 11){
24755                     this.vIndex = 11;
24756                 }
24757                 
24758                 if(isNaN(this.vIndex)){
24759                     this.vIndex = 0;
24760                 }
24761                 
24762                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24763                 break;
24764                 
24765             case 13: // enter
24766                 
24767                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24768                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24769                 }
24770                 
24771                 this.hide();
24772                 e.preventDefault();
24773                 break;
24774             case 9: // tab
24775                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24776                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24777                 }
24778                 this.hide();
24779                 break;
24780             case 16: // shift
24781             case 17: // ctrl
24782             case 18: // alt
24783                 break;
24784             default :
24785                 this.hide();
24786                 
24787         }
24788     },
24789     
24790     remove: function() 
24791     {
24792         this.picker().remove();
24793     }
24794    
24795 });
24796
24797 Roo.apply(Roo.bootstrap.form.MonthField,  {
24798     
24799     content : {
24800         tag: 'tbody',
24801         cn: [
24802         {
24803             tag: 'tr',
24804             cn: [
24805             {
24806                 tag: 'td',
24807                 colspan: '7'
24808             }
24809             ]
24810         }
24811         ]
24812     },
24813     
24814     dates:{
24815         en: {
24816             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24817             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24818         }
24819     }
24820 });
24821
24822 Roo.apply(Roo.bootstrap.form.MonthField,  {
24823   
24824     template : {
24825         tag: 'div',
24826         cls: 'datepicker dropdown-menu roo-dynamic',
24827         cn: [
24828             {
24829                 tag: 'div',
24830                 cls: 'datepicker-months',
24831                 cn: [
24832                 {
24833                     tag: 'table',
24834                     cls: 'table-condensed',
24835                     cn:[
24836                         Roo.bootstrap.form.DateField.content
24837                     ]
24838                 }
24839                 ]
24840             }
24841         ]
24842     }
24843 });
24844
24845  
24846
24847  
24848  /*
24849  * - LGPL
24850  *
24851  * CheckBox
24852  * 
24853  */
24854
24855 /**
24856  * @class Roo.bootstrap.form.CheckBox
24857  * @extends Roo.bootstrap.form.Input
24858  * Bootstrap CheckBox class
24859  * 
24860  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24861  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24862  * @cfg {String} boxLabel The text that appears beside the checkbox
24863  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24864  * @cfg {Boolean} checked initnal the element
24865  * @cfg {Boolean} inline inline the element (default false)
24866  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24867  * @cfg {String} tooltip label tooltip
24868  * 
24869  * @constructor
24870  * Create a new CheckBox
24871  * @param {Object} config The config object
24872  */
24873
24874 Roo.bootstrap.form.CheckBox = function(config){
24875     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24876    
24877     this.addEvents({
24878         /**
24879         * @event check
24880         * Fires when the element is checked or unchecked.
24881         * @param {Roo.bootstrap.form.CheckBox} this This input
24882         * @param {Boolean} checked The new checked value
24883         */
24884        check : true,
24885        /**
24886         * @event click
24887         * Fires when the element is click.
24888         * @param {Roo.bootstrap.form.CheckBox} this This input
24889         */
24890        click : true
24891     });
24892     
24893 };
24894
24895 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24896   
24897     inputType: 'checkbox',
24898     inputValue: 1,
24899     valueOff: 0,
24900     boxLabel: false,
24901     checked: false,
24902     weight : false,
24903     inline: false,
24904     tooltip : '',
24905     
24906     // checkbox success does not make any sense really.. 
24907     invalidClass : "",
24908     validClass : "",
24909     
24910     
24911     getAutoCreate : function()
24912     {
24913         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24914         
24915         var id = Roo.id();
24916         
24917         var cfg = {};
24918         
24919         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24920         
24921         if(this.inline){
24922             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24923         }
24924         
24925         var input =  {
24926             tag: 'input',
24927             id : id,
24928             type : this.inputType,
24929             value : this.inputValue,
24930             cls : 'roo-' + this.inputType, //'form-box',
24931             placeholder : this.placeholder || ''
24932             
24933         };
24934         
24935         if(this.inputType != 'radio'){
24936             var hidden =  {
24937                 tag: 'input',
24938                 type : 'hidden',
24939                 cls : 'roo-hidden-value',
24940                 value : this.checked ? this.inputValue : this.valueOff
24941             };
24942         }
24943         
24944             
24945         if (this.weight) { // Validity check?
24946             cfg.cls += " " + this.inputType + "-" + this.weight;
24947         }
24948         
24949         if (this.disabled) {
24950             input.disabled=true;
24951         }
24952         
24953         if(this.checked){
24954             input.checked = this.checked;
24955         }
24956         
24957         if (this.name) {
24958             
24959             input.name = this.name;
24960             
24961             if(this.inputType != 'radio'){
24962                 hidden.name = this.name;
24963                 input.name = '_hidden_' + this.name;
24964             }
24965         }
24966         
24967         if (this.size) {
24968             input.cls += ' input-' + this.size;
24969         }
24970         
24971         var settings=this;
24972         
24973         ['xs','sm','md','lg'].map(function(size){
24974             if (settings[size]) {
24975                 cfg.cls += ' col-' + size + '-' + settings[size];
24976             }
24977         });
24978         
24979         var inputblock = input;
24980          
24981         if (this.before || this.after) {
24982             
24983             inputblock = {
24984                 cls : 'input-group',
24985                 cn :  [] 
24986             };
24987             
24988             if (this.before) {
24989                 inputblock.cn.push({
24990                     tag :'span',
24991                     cls : 'input-group-addon',
24992                     html : this.before
24993                 });
24994             }
24995             
24996             inputblock.cn.push(input);
24997             
24998             if(this.inputType != 'radio'){
24999                 inputblock.cn.push(hidden);
25000             }
25001             
25002             if (this.after) {
25003                 inputblock.cn.push({
25004                     tag :'span',
25005                     cls : 'input-group-addon',
25006                     html : this.after
25007                 });
25008             }
25009             
25010         }
25011         var boxLabelCfg = false;
25012         
25013         if(this.boxLabel){
25014            
25015             boxLabelCfg = {
25016                 tag: 'label',
25017                 //'for': id, // box label is handled by onclick - so no for...
25018                 cls: 'box-label',
25019                 html: this.boxLabel
25020             };
25021             if(this.tooltip){
25022                 boxLabelCfg.tooltip = this.tooltip;
25023             }
25024              
25025         }
25026         
25027         
25028         if (align ==='left' && this.fieldLabel.length) {
25029 //                Roo.log("left and has label");
25030             cfg.cn = [
25031                 {
25032                     tag: 'label',
25033                     'for' :  id,
25034                     cls : 'control-label',
25035                     html : this.fieldLabel
25036                 },
25037                 {
25038                     cls : "", 
25039                     cn: [
25040                         inputblock
25041                     ]
25042                 }
25043             ];
25044             
25045             if (boxLabelCfg) {
25046                 cfg.cn[1].cn.push(boxLabelCfg);
25047             }
25048             
25049             if(this.labelWidth > 12){
25050                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25051             }
25052             
25053             if(this.labelWidth < 13 && this.labelmd == 0){
25054                 this.labelmd = this.labelWidth;
25055             }
25056             
25057             if(this.labellg > 0){
25058                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25059                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25060             }
25061             
25062             if(this.labelmd > 0){
25063                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25064                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25065             }
25066             
25067             if(this.labelsm > 0){
25068                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25069                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25070             }
25071             
25072             if(this.labelxs > 0){
25073                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25074                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25075             }
25076             
25077         } else if ( this.fieldLabel.length) {
25078 //                Roo.log(" label");
25079                 cfg.cn = [
25080                    
25081                     {
25082                         tag: this.boxLabel ? 'span' : 'label',
25083                         'for': id,
25084                         cls: 'control-label box-input-label',
25085                         //cls : 'input-group-addon',
25086                         html : this.fieldLabel
25087                     },
25088                     
25089                     inputblock
25090                     
25091                 ];
25092                 if (boxLabelCfg) {
25093                     cfg.cn.push(boxLabelCfg);
25094                 }
25095
25096         } else {
25097             
25098 //                Roo.log(" no label && no align");
25099                 cfg.cn = [  inputblock ] ;
25100                 if (boxLabelCfg) {
25101                     cfg.cn.push(boxLabelCfg);
25102                 }
25103
25104                 
25105         }
25106         
25107        
25108         
25109         if(this.inputType != 'radio'){
25110             cfg.cn.push(hidden);
25111         }
25112         
25113         return cfg;
25114         
25115     },
25116     
25117     /**
25118      * return the real input element.
25119      */
25120     inputEl: function ()
25121     {
25122         return this.el.select('input.roo-' + this.inputType,true).first();
25123     },
25124     hiddenEl: function ()
25125     {
25126         return this.el.select('input.roo-hidden-value',true).first();
25127     },
25128     
25129     labelEl: function()
25130     {
25131         return this.el.select('label.control-label',true).first();
25132     },
25133     /* depricated... */
25134     
25135     label: function()
25136     {
25137         return this.labelEl();
25138     },
25139     
25140     boxLabelEl: function()
25141     {
25142         return this.el.select('label.box-label',true).first();
25143     },
25144     
25145     initEvents : function()
25146     {
25147 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25148         
25149         this.inputEl().on('click', this.onClick,  this);
25150         
25151         if (this.boxLabel) { 
25152             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25153         }
25154         
25155         this.startValue = this.getValue();
25156         
25157         if(this.groupId){
25158             Roo.bootstrap.form.CheckBox.register(this);
25159         }
25160     },
25161     
25162     onClick : function(e)
25163     {   
25164         if(this.fireEvent('click', this, e) !== false){
25165             this.setChecked(!this.checked);
25166         }
25167         
25168     },
25169     
25170     setChecked : function(state,suppressEvent)
25171     {
25172         this.startValue = this.getValue();
25173
25174         if(this.inputType == 'radio'){
25175             
25176             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25177                 e.dom.checked = false;
25178             });
25179             
25180             this.inputEl().dom.checked = true;
25181             
25182             this.inputEl().dom.value = this.inputValue;
25183             
25184             if(suppressEvent !== true){
25185                 this.fireEvent('check', this, true);
25186             }
25187             
25188             this.validate();
25189             
25190             return;
25191         }
25192         
25193         this.checked = state;
25194         
25195         this.inputEl().dom.checked = state;
25196         
25197         
25198         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25199         
25200         if(suppressEvent !== true){
25201             this.fireEvent('check', this, state);
25202         }
25203         
25204         this.validate();
25205     },
25206     
25207     getValue : function()
25208     {
25209         if(this.inputType == 'radio'){
25210             return this.getGroupValue();
25211         }
25212         
25213         return this.hiddenEl().dom.value;
25214         
25215     },
25216     
25217     getGroupValue : function()
25218     {
25219         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25220             return '';
25221         }
25222         
25223         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25224     },
25225     
25226     setValue : function(v,suppressEvent)
25227     {
25228         if(this.inputType == 'radio'){
25229             this.setGroupValue(v, suppressEvent);
25230             return;
25231         }
25232         
25233         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25234         
25235         this.validate();
25236     },
25237     
25238     setGroupValue : function(v, suppressEvent)
25239     {
25240         this.startValue = this.getValue();
25241         
25242         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25243             e.dom.checked = false;
25244             
25245             if(e.dom.value == v){
25246                 e.dom.checked = true;
25247             }
25248         });
25249         
25250         if(suppressEvent !== true){
25251             this.fireEvent('check', this, true);
25252         }
25253
25254         this.validate();
25255         
25256         return;
25257     },
25258     
25259     validate : function()
25260     {
25261         if(this.getVisibilityEl().hasClass('hidden')){
25262             return true;
25263         }
25264         
25265         if(
25266                 this.disabled || 
25267                 (this.inputType == 'radio' && this.validateRadio()) ||
25268                 (this.inputType == 'checkbox' && this.validateCheckbox())
25269         ){
25270             this.markValid();
25271             return true;
25272         }
25273         
25274         this.markInvalid();
25275         return false;
25276     },
25277     
25278     validateRadio : function()
25279     {
25280         if(this.getVisibilityEl().hasClass('hidden')){
25281             return true;
25282         }
25283         
25284         if(this.allowBlank){
25285             return true;
25286         }
25287         
25288         var valid = false;
25289         
25290         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25291             if(!e.dom.checked){
25292                 return;
25293             }
25294             
25295             valid = true;
25296             
25297             return false;
25298         });
25299         
25300         return valid;
25301     },
25302     
25303     validateCheckbox : function()
25304     {
25305         if(!this.groupId){
25306             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25307             //return (this.getValue() == this.inputValue) ? true : false;
25308         }
25309         
25310         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25311         
25312         if(!group){
25313             return false;
25314         }
25315         
25316         var r = false;
25317         
25318         for(var i in group){
25319             if(group[i].el.isVisible(true)){
25320                 r = false;
25321                 break;
25322             }
25323             
25324             r = true;
25325         }
25326         
25327         for(var i in group){
25328             if(r){
25329                 break;
25330             }
25331             
25332             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25333         }
25334         
25335         return r;
25336     },
25337     
25338     /**
25339      * Mark this field as valid
25340      */
25341     markValid : function()
25342     {
25343         var _this = this;
25344         
25345         this.fireEvent('valid', this);
25346         
25347         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25348         
25349         if(this.groupId){
25350             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25351         }
25352         
25353         if(label){
25354             label.markValid();
25355         }
25356
25357         if(this.inputType == 'radio'){
25358             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25359                 var fg = e.findParent('.form-group', false, true);
25360                 if (Roo.bootstrap.version == 3) {
25361                     fg.removeClass([_this.invalidClass, _this.validClass]);
25362                     fg.addClass(_this.validClass);
25363                 } else {
25364                     fg.removeClass(['is-valid', 'is-invalid']);
25365                     fg.addClass('is-valid');
25366                 }
25367             });
25368             
25369             return;
25370         }
25371
25372         if(!this.groupId){
25373             var fg = this.el.findParent('.form-group', false, true);
25374             if (Roo.bootstrap.version == 3) {
25375                 fg.removeClass([this.invalidClass, this.validClass]);
25376                 fg.addClass(this.validClass);
25377             } else {
25378                 fg.removeClass(['is-valid', 'is-invalid']);
25379                 fg.addClass('is-valid');
25380             }
25381             return;
25382         }
25383         
25384         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25385         
25386         if(!group){
25387             return;
25388         }
25389         
25390         for(var i in group){
25391             var fg = group[i].el.findParent('.form-group', false, true);
25392             if (Roo.bootstrap.version == 3) {
25393                 fg.removeClass([this.invalidClass, this.validClass]);
25394                 fg.addClass(this.validClass);
25395             } else {
25396                 fg.removeClass(['is-valid', 'is-invalid']);
25397                 fg.addClass('is-valid');
25398             }
25399         }
25400     },
25401     
25402      /**
25403      * Mark this field as invalid
25404      * @param {String} msg The validation message
25405      */
25406     markInvalid : function(msg)
25407     {
25408         if(this.allowBlank){
25409             return;
25410         }
25411         
25412         var _this = this;
25413         
25414         this.fireEvent('invalid', this, msg);
25415         
25416         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25417         
25418         if(this.groupId){
25419             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25420         }
25421         
25422         if(label){
25423             label.markInvalid();
25424         }
25425             
25426         if(this.inputType == 'radio'){
25427             
25428             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25429                 var fg = e.findParent('.form-group', false, true);
25430                 if (Roo.bootstrap.version == 3) {
25431                     fg.removeClass([_this.invalidClass, _this.validClass]);
25432                     fg.addClass(_this.invalidClass);
25433                 } else {
25434                     fg.removeClass(['is-invalid', 'is-valid']);
25435                     fg.addClass('is-invalid');
25436                 }
25437             });
25438             
25439             return;
25440         }
25441         
25442         if(!this.groupId){
25443             var fg = this.el.findParent('.form-group', false, true);
25444             if (Roo.bootstrap.version == 3) {
25445                 fg.removeClass([_this.invalidClass, _this.validClass]);
25446                 fg.addClass(_this.invalidClass);
25447             } else {
25448                 fg.removeClass(['is-invalid', 'is-valid']);
25449                 fg.addClass('is-invalid');
25450             }
25451             return;
25452         }
25453         
25454         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25455         
25456         if(!group){
25457             return;
25458         }
25459         
25460         for(var i in group){
25461             var fg = group[i].el.findParent('.form-group', false, true);
25462             if (Roo.bootstrap.version == 3) {
25463                 fg.removeClass([_this.invalidClass, _this.validClass]);
25464                 fg.addClass(_this.invalidClass);
25465             } else {
25466                 fg.removeClass(['is-invalid', 'is-valid']);
25467                 fg.addClass('is-invalid');
25468             }
25469         }
25470         
25471     },
25472     
25473     clearInvalid : function()
25474     {
25475         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25476         
25477         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25478         
25479         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25480         
25481         if (label && label.iconEl) {
25482             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25483             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25484         }
25485     },
25486     
25487     disable : function()
25488     {
25489         if(this.inputType != 'radio'){
25490             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25491             return;
25492         }
25493         
25494         var _this = this;
25495         
25496         if(this.rendered){
25497             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25498                 _this.getActionEl().addClass(this.disabledClass);
25499                 e.dom.disabled = true;
25500             });
25501         }
25502         
25503         this.disabled = true;
25504         this.fireEvent("disable", this);
25505         return this;
25506     },
25507
25508     enable : function()
25509     {
25510         if(this.inputType != 'radio'){
25511             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25512             return;
25513         }
25514         
25515         var _this = this;
25516         
25517         if(this.rendered){
25518             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25519                 _this.getActionEl().removeClass(this.disabledClass);
25520                 e.dom.disabled = false;
25521             });
25522         }
25523         
25524         this.disabled = false;
25525         this.fireEvent("enable", this);
25526         return this;
25527     },
25528     
25529     setBoxLabel : function(v)
25530     {
25531         this.boxLabel = v;
25532         
25533         if(this.rendered){
25534             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25535         }
25536     }
25537
25538 });
25539
25540 Roo.apply(Roo.bootstrap.form.CheckBox, {
25541     
25542     groups: {},
25543     
25544      /**
25545     * register a CheckBox Group
25546     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25547     */
25548     register : function(checkbox)
25549     {
25550         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25551             this.groups[checkbox.groupId] = {};
25552         }
25553         
25554         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25555             return;
25556         }
25557         
25558         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25559         
25560     },
25561     /**
25562     * fetch a CheckBox Group based on the group ID
25563     * @param {string} the group ID
25564     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25565     */
25566     get: function(groupId) {
25567         if (typeof(this.groups[groupId]) == 'undefined') {
25568             return false;
25569         }
25570         
25571         return this.groups[groupId] ;
25572     }
25573     
25574     
25575 });
25576 /*
25577  * - LGPL
25578  *
25579  * RadioItem
25580  * 
25581  */
25582
25583 /**
25584  * @class Roo.bootstrap.form.Radio
25585  * @extends Roo.bootstrap.Component
25586  * Bootstrap Radio class
25587  * @cfg {String} boxLabel - the label associated
25588  * @cfg {String} value - the value of radio
25589  * 
25590  * @constructor
25591  * Create a new Radio
25592  * @param {Object} config The config object
25593  */
25594 Roo.bootstrap.form.Radio = function(config){
25595     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25596     
25597 };
25598
25599 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25600     
25601     boxLabel : '',
25602     
25603     value : '',
25604     
25605     getAutoCreate : function()
25606     {
25607         var cfg = {
25608             tag : 'div',
25609             cls : 'form-group radio',
25610             cn : [
25611                 {
25612                     tag : 'label',
25613                     cls : 'box-label',
25614                     html : this.boxLabel
25615                 }
25616             ]
25617         };
25618         
25619         return cfg;
25620     },
25621     
25622     initEvents : function() 
25623     {
25624         this.parent().register(this);
25625         
25626         this.el.on('click', this.onClick, this);
25627         
25628     },
25629     
25630     onClick : function(e)
25631     {
25632         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25633             this.setChecked(true);
25634         }
25635     },
25636     
25637     setChecked : function(state, suppressEvent)
25638     {
25639         this.parent().setValue(this.value, suppressEvent);
25640         
25641     },
25642     
25643     setBoxLabel : function(v)
25644     {
25645         this.boxLabel = v;
25646         
25647         if(this.rendered){
25648             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25649         }
25650     }
25651     
25652 });
25653  
25654
25655  /*
25656  * - LGPL
25657  *
25658  * Input
25659  * 
25660  */
25661
25662 /**
25663  * @class Roo.bootstrap.form.SecurePass
25664  * @extends Roo.bootstrap.form.Input
25665  * Bootstrap SecurePass class
25666  *
25667  * 
25668  * @constructor
25669  * Create a new SecurePass
25670  * @param {Object} config The config object
25671  */
25672  
25673 Roo.bootstrap.form.SecurePass = function (config) {
25674     // these go here, so the translation tool can replace them..
25675     this.errors = {
25676         PwdEmpty: "Please type a password, and then retype it to confirm.",
25677         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25678         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25679         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25680         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25681         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25682         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25683         TooWeak: "Your password is Too Weak."
25684     },
25685     this.meterLabel = "Password strength:";
25686     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25687     this.meterClass = [
25688         "roo-password-meter-tooweak", 
25689         "roo-password-meter-weak", 
25690         "roo-password-meter-medium", 
25691         "roo-password-meter-strong", 
25692         "roo-password-meter-grey"
25693     ];
25694     
25695     this.errors = {};
25696     
25697     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25698 }
25699
25700 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25701     /**
25702      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25703      * {
25704      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25705      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25706      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25707      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25708      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25709      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25710      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25711      * })
25712      */
25713     // private
25714     
25715     meterWidth: 300,
25716     errorMsg :'',    
25717     errors: false,
25718     imageRoot: '/',
25719     /**
25720      * @cfg {String/Object} Label for the strength meter (defaults to
25721      * 'Password strength:')
25722      */
25723     // private
25724     meterLabel: '',
25725     /**
25726      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25727      * ['Weak', 'Medium', 'Strong'])
25728      */
25729     // private    
25730     pwdStrengths: false,    
25731     // private
25732     strength: 0,
25733     // private
25734     _lastPwd: null,
25735     // private
25736     kCapitalLetter: 0,
25737     kSmallLetter: 1,
25738     kDigit: 2,
25739     kPunctuation: 3,
25740     
25741     insecure: false,
25742     // private
25743     initEvents: function ()
25744     {
25745         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25746
25747         if (this.el.is('input[type=password]') && Roo.isSafari) {
25748             this.el.on('keydown', this.SafariOnKeyDown, this);
25749         }
25750
25751         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25752     },
25753     // private
25754     onRender: function (ct, position)
25755     {
25756         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25757         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25758         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25759
25760         this.trigger.createChild({
25761                    cn: [
25762                     {
25763                     //id: 'PwdMeter',
25764                     tag: 'div',
25765                     cls: 'roo-password-meter-grey col-xs-12',
25766                     style: {
25767                         //width: 0,
25768                         //width: this.meterWidth + 'px'                                                
25769                         }
25770                     },
25771                     {                            
25772                          cls: 'roo-password-meter-text'                          
25773                     }
25774                 ]            
25775         });
25776
25777          
25778         if (this.hideTrigger) {
25779             this.trigger.setDisplayed(false);
25780         }
25781         this.setSize(this.width || '', this.height || '');
25782     },
25783     // private
25784     onDestroy: function ()
25785     {
25786         if (this.trigger) {
25787             this.trigger.removeAllListeners();
25788             this.trigger.remove();
25789         }
25790         if (this.wrap) {
25791             this.wrap.remove();
25792         }
25793         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25794     },
25795     // private
25796     checkStrength: function ()
25797     {
25798         var pwd = this.inputEl().getValue();
25799         if (pwd == this._lastPwd) {
25800             return;
25801         }
25802
25803         var strength;
25804         if (this.ClientSideStrongPassword(pwd)) {
25805             strength = 3;
25806         } else if (this.ClientSideMediumPassword(pwd)) {
25807             strength = 2;
25808         } else if (this.ClientSideWeakPassword(pwd)) {
25809             strength = 1;
25810         } else {
25811             strength = 0;
25812         }
25813         
25814         Roo.log('strength1: ' + strength);
25815         
25816         //var pm = this.trigger.child('div/div/div').dom;
25817         var pm = this.trigger.child('div/div');
25818         pm.removeClass(this.meterClass);
25819         pm.addClass(this.meterClass[strength]);
25820                 
25821         
25822         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25823                 
25824         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25825         
25826         this._lastPwd = pwd;
25827     },
25828     reset: function ()
25829     {
25830         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25831         
25832         this._lastPwd = '';
25833         
25834         var pm = this.trigger.child('div/div');
25835         pm.removeClass(this.meterClass);
25836         pm.addClass('roo-password-meter-grey');        
25837         
25838         
25839         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25840         
25841         pt.innerHTML = '';
25842         this.inputEl().dom.type='password';
25843     },
25844     // private
25845     validateValue: function (value)
25846     {
25847         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25848             return false;
25849         }
25850         if (value.length == 0) {
25851             if (this.allowBlank) {
25852                 this.clearInvalid();
25853                 return true;
25854             }
25855
25856             this.markInvalid(this.errors.PwdEmpty);
25857             this.errorMsg = this.errors.PwdEmpty;
25858             return false;
25859         }
25860         
25861         if(this.insecure){
25862             return true;
25863         }
25864         
25865         if (!value.match(/[\x21-\x7e]+/)) {
25866             this.markInvalid(this.errors.PwdBadChar);
25867             this.errorMsg = this.errors.PwdBadChar;
25868             return false;
25869         }
25870         if (value.length < 6) {
25871             this.markInvalid(this.errors.PwdShort);
25872             this.errorMsg = this.errors.PwdShort;
25873             return false;
25874         }
25875         if (value.length > 16) {
25876             this.markInvalid(this.errors.PwdLong);
25877             this.errorMsg = this.errors.PwdLong;
25878             return false;
25879         }
25880         var strength;
25881         if (this.ClientSideStrongPassword(value)) {
25882             strength = 3;
25883         } else if (this.ClientSideMediumPassword(value)) {
25884             strength = 2;
25885         } else if (this.ClientSideWeakPassword(value)) {
25886             strength = 1;
25887         } else {
25888             strength = 0;
25889         }
25890
25891         
25892         if (strength < 2) {
25893             //this.markInvalid(this.errors.TooWeak);
25894             this.errorMsg = this.errors.TooWeak;
25895             //return false;
25896         }
25897         
25898         
25899         console.log('strength2: ' + strength);
25900         
25901         //var pm = this.trigger.child('div/div/div').dom;
25902         
25903         var pm = this.trigger.child('div/div');
25904         pm.removeClass(this.meterClass);
25905         pm.addClass(this.meterClass[strength]);
25906                 
25907         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25908                 
25909         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25910         
25911         this.errorMsg = ''; 
25912         return true;
25913     },
25914     // private
25915     CharacterSetChecks: function (type)
25916     {
25917         this.type = type;
25918         this.fResult = false;
25919     },
25920     // private
25921     isctype: function (character, type)
25922     {
25923         switch (type) {  
25924             case this.kCapitalLetter:
25925                 if (character >= 'A' && character <= 'Z') {
25926                     return true;
25927                 }
25928                 break;
25929             
25930             case this.kSmallLetter:
25931                 if (character >= 'a' && character <= 'z') {
25932                     return true;
25933                 }
25934                 break;
25935             
25936             case this.kDigit:
25937                 if (character >= '0' && character <= '9') {
25938                     return true;
25939                 }
25940                 break;
25941             
25942             case this.kPunctuation:
25943                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25944                     return true;
25945                 }
25946                 break;
25947             
25948             default:
25949                 return false;
25950         }
25951
25952     },
25953     // private
25954     IsLongEnough: function (pwd, size)
25955     {
25956         return !(pwd == null || isNaN(size) || pwd.length < size);
25957     },
25958     // private
25959     SpansEnoughCharacterSets: function (word, nb)
25960     {
25961         if (!this.IsLongEnough(word, nb))
25962         {
25963             return false;
25964         }
25965
25966         var characterSetChecks = new Array(
25967             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25968             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25969         );
25970         
25971         for (var index = 0; index < word.length; ++index) {
25972             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25973                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25974                     characterSetChecks[nCharSet].fResult = true;
25975                     break;
25976                 }
25977             }
25978         }
25979
25980         var nCharSets = 0;
25981         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25982             if (characterSetChecks[nCharSet].fResult) {
25983                 ++nCharSets;
25984             }
25985         }
25986
25987         if (nCharSets < nb) {
25988             return false;
25989         }
25990         return true;
25991     },
25992     // private
25993     ClientSideStrongPassword: function (pwd)
25994     {
25995         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25996     },
25997     // private
25998     ClientSideMediumPassword: function (pwd)
25999     {
26000         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26001     },
26002     // private
26003     ClientSideWeakPassword: function (pwd)
26004     {
26005         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26006     }
26007           
26008 });
26009 Roo.htmleditor = {};
26010  
26011 /**
26012  * @class Roo.htmleditor.Filter
26013  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26014  * @cfg {DomElement} node The node to iterate and filter
26015  * @cfg {boolean|String|Array} tag Tags to replace 
26016  * @constructor
26017  * Create a new Filter.
26018  * @param {Object} config Configuration options
26019  */
26020
26021
26022
26023 Roo.htmleditor.Filter = function(cfg) {
26024     Roo.apply(this.cfg);
26025     // this does not actually call walk as it's really just a abstract class
26026 }
26027
26028
26029 Roo.htmleditor.Filter.prototype = {
26030     
26031     node: false,
26032     
26033     tag: false,
26034
26035     // overrride to do replace comments.
26036     replaceComment : false,
26037     
26038     // overrride to do replace or do stuff with tags..
26039     replaceTag : false,
26040     
26041     walk : function(dom)
26042     {
26043         Roo.each( Array.from(dom.childNodes), function( e ) {
26044             switch(true) {
26045                 
26046                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26047                     this.replaceComment(e);
26048                     return;
26049                 
26050                 case e.nodeType != 1: //not a node.
26051                     return;
26052                 
26053                 case this.tag === true: // everything
26054                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26055                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26056                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26057                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26058                     if (this.replaceTag && false === this.replaceTag(e)) {
26059                         return;
26060                     }
26061                     if (e.hasChildNodes()) {
26062                         this.walk(e);
26063                     }
26064                     return;
26065                 
26066                 default:    // tags .. that do not match.
26067                     if (e.hasChildNodes()) {
26068                         this.walk(e);
26069                     }
26070             }
26071             
26072         }, this);
26073         
26074     },
26075     
26076     
26077     removeNodeKeepChildren : function( node)
26078     {
26079     
26080         ar = Array.from(node.childNodes);
26081         for (var i = 0; i < ar.length; i++) {
26082          
26083             node.removeChild(ar[i]);
26084             // what if we need to walk these???
26085             node.parentNode.insertBefore(ar[i], node);
26086            
26087         }
26088         node.parentNode.removeChild(node);
26089     }
26090 }; 
26091
26092 /**
26093  * @class Roo.htmleditor.FilterAttributes
26094  * clean attributes and  styles including http:// etc.. in attribute
26095  * @constructor
26096 * Run a new Attribute Filter
26097 * @param {Object} config Configuration options
26098  */
26099 Roo.htmleditor.FilterAttributes = function(cfg)
26100 {
26101     Roo.apply(this, cfg);
26102     this.attrib_black = this.attrib_black || [];
26103     this.attrib_white = this.attrib_white || [];
26104
26105     this.attrib_clean = this.attrib_clean || [];
26106     this.style_white = this.style_white || [];
26107     this.style_black = this.style_black || [];
26108     this.walk(cfg.node);
26109 }
26110
26111 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26112 {
26113     tag: true, // all tags
26114     
26115     attrib_black : false, // array
26116     attrib_clean : false,
26117     attrib_white : false,
26118
26119     style_white : false,
26120     style_black : false,
26121      
26122      
26123     replaceTag : function(node)
26124     {
26125         if (!node.attributes || !node.attributes.length) {
26126             return true;
26127         }
26128         
26129         for (var i = node.attributes.length-1; i > -1 ; i--) {
26130             var a = node.attributes[i];
26131             //console.log(a);
26132             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26133                 node.removeAttribute(a.name);
26134                 continue;
26135             }
26136             
26137             
26138             
26139             if (a.name.toLowerCase().substr(0,2)=='on')  {
26140                 node.removeAttribute(a.name);
26141                 continue;
26142             }
26143             
26144             
26145             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26146                 node.removeAttribute(a.name);
26147                 continue;
26148             }
26149             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26150                 this.cleanAttr(node,a.name,a.value); // fixme..
26151                 continue;
26152             }
26153             if (a.name == 'style') {
26154                 this.cleanStyle(node,a.name,a.value);
26155                 continue;
26156             }
26157             /// clean up MS crap..
26158             // tecnically this should be a list of valid class'es..
26159             
26160             
26161             if (a.name == 'class') {
26162                 if (a.value.match(/^Mso/)) {
26163                     node.removeAttribute('class');
26164                 }
26165                 
26166                 if (a.value.match(/^body$/)) {
26167                     node.removeAttribute('class');
26168                 }
26169                 continue;
26170             }
26171             
26172             
26173             // style cleanup!?
26174             // class cleanup?
26175             
26176         }
26177         return true; // clean children
26178     },
26179         
26180     cleanAttr: function(node, n,v)
26181     {
26182         
26183         if (v.match(/^\./) || v.match(/^\//)) {
26184             return;
26185         }
26186         if (v.match(/^(http|https):\/\//)
26187             || v.match(/^mailto:/) 
26188             || v.match(/^ftp:/)
26189             || v.match(/^data:/)
26190             ) {
26191             return;
26192         }
26193         if (v.match(/^#/)) {
26194             return;
26195         }
26196         if (v.match(/^\{/)) { // allow template editing.
26197             return;
26198         }
26199 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26200         node.removeAttribute(n);
26201         
26202     },
26203     cleanStyle : function(node,  n,v)
26204     {
26205         if (v.match(/expression/)) { //XSS?? should we even bother..
26206             node.removeAttribute(n);
26207             return;
26208         }
26209         
26210         var parts = v.split(/;/);
26211         var clean = [];
26212         
26213         Roo.each(parts, function(p) {
26214             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26215             if (!p.length) {
26216                 return true;
26217             }
26218             var l = p.split(':').shift().replace(/\s+/g,'');
26219             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26220             
26221             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26222                 return true;
26223             }
26224             //Roo.log()
26225             // only allow 'c whitelisted system attributes'
26226             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26227                 return true;
26228             }
26229             
26230             
26231             clean.push(p);
26232             return true;
26233         },this);
26234         if (clean.length) { 
26235             node.setAttribute(n, clean.join(';'));
26236         } else {
26237             node.removeAttribute(n);
26238         }
26239         
26240     }
26241         
26242         
26243         
26244     
26245 });/**
26246  * @class Roo.htmleditor.FilterBlack
26247  * remove blacklisted elements.
26248  * @constructor
26249  * Run a new Blacklisted Filter
26250  * @param {Object} config Configuration options
26251  */
26252
26253 Roo.htmleditor.FilterBlack = function(cfg)
26254 {
26255     Roo.apply(this, cfg);
26256     this.walk(cfg.node);
26257 }
26258
26259 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26260 {
26261     tag : true, // all elements.
26262    
26263     replaceTag : function(n)
26264     {
26265         n.parentNode.removeChild(n);
26266     }
26267 });
26268 /**
26269  * @class Roo.htmleditor.FilterComment
26270  * remove comments.
26271  * @constructor
26272 * Run a new Comments Filter
26273 * @param {Object} config Configuration options
26274  */
26275 Roo.htmleditor.FilterComment = function(cfg)
26276 {
26277     this.walk(cfg.node);
26278 }
26279
26280 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26281 {
26282   
26283     replaceComment : function(n)
26284     {
26285         n.parentNode.removeChild(n);
26286     }
26287 });/**
26288  * @class Roo.htmleditor.FilterKeepChildren
26289  * remove tags but keep children
26290  * @constructor
26291  * Run a new Keep Children Filter
26292  * @param {Object} config Configuration options
26293  */
26294
26295 Roo.htmleditor.FilterKeepChildren = function(cfg)
26296 {
26297     Roo.apply(this, cfg);
26298     if (this.tag === false) {
26299         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26300     }
26301     // hacky?
26302     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26303         this.cleanNamespace = true;
26304     }
26305         
26306     this.walk(cfg.node);
26307 }
26308
26309 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26310 {
26311     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26312   
26313     replaceTag : function(node)
26314     {
26315         // walk children...
26316         //Roo.log(node.tagName);
26317         var ar = Array.from(node.childNodes);
26318         //remove first..
26319         
26320         for (var i = 0; i < ar.length; i++) {
26321             var e = ar[i];
26322             if (e.nodeType == 1) {
26323                 if (
26324                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26325                     || // array and it matches
26326                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26327                     ||
26328                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26329                     ||
26330                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26331                 ) {
26332                     this.replaceTag(ar[i]); // child is blacklisted as well...
26333                     continue;
26334                 }
26335             }
26336         }  
26337         ar = Array.from(node.childNodes);
26338         for (var i = 0; i < ar.length; i++) {
26339          
26340             node.removeChild(ar[i]);
26341             // what if we need to walk these???
26342             node.parentNode.insertBefore(ar[i], node);
26343             if (this.tag !== false) {
26344                 this.walk(ar[i]);
26345                 
26346             }
26347         }
26348         //Roo.log("REMOVE:" + node.tagName);
26349         node.parentNode.removeChild(node);
26350         return false; // don't walk children
26351         
26352         
26353     }
26354 });/**
26355  * @class Roo.htmleditor.FilterParagraph
26356  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26357  * like on 'push' to remove the <p> tags and replace them with line breaks.
26358  * @constructor
26359  * Run a new Paragraph Filter
26360  * @param {Object} config Configuration options
26361  */
26362
26363 Roo.htmleditor.FilterParagraph = function(cfg)
26364 {
26365     // no need to apply config.
26366     this.walk(cfg.node);
26367 }
26368
26369 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26370 {
26371     
26372      
26373     tag : 'P',
26374     
26375      
26376     replaceTag : function(node)
26377     {
26378         
26379         if (node.childNodes.length == 1 &&
26380             node.childNodes[0].nodeType == 3 &&
26381             node.childNodes[0].textContent.trim().length < 1
26382             ) {
26383             // remove and replace with '<BR>';
26384             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26385             return false; // no need to walk..
26386         }
26387         var ar = Array.from(node.childNodes);
26388         for (var i = 0; i < ar.length; i++) {
26389             node.removeChild(ar[i]);
26390             // what if we need to walk these???
26391             node.parentNode.insertBefore(ar[i], node);
26392         }
26393         // now what about this?
26394         // <p> &nbsp; </p>
26395         
26396         // double BR.
26397         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26398         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26399         node.parentNode.removeChild(node);
26400         
26401         return false;
26402
26403     }
26404     
26405 });/**
26406  * @class Roo.htmleditor.FilterSpan
26407  * filter span's with no attributes out..
26408  * @constructor
26409  * Run a new Span Filter
26410  * @param {Object} config Configuration options
26411  */
26412
26413 Roo.htmleditor.FilterSpan = function(cfg)
26414 {
26415     // no need to apply config.
26416     this.walk(cfg.node);
26417 }
26418
26419 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26420 {
26421      
26422     tag : 'SPAN',
26423      
26424  
26425     replaceTag : function(node)
26426     {
26427         if (node.attributes && node.attributes.length > 0) {
26428             return true; // walk if there are any.
26429         }
26430         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26431         return false;
26432      
26433     }
26434     
26435 });/**
26436  * @class Roo.htmleditor.FilterTableWidth
26437   try and remove table width data - as that frequently messes up other stuff.
26438  * 
26439  *      was cleanTableWidths.
26440  *
26441  * Quite often pasting from word etc.. results in tables with column and widths.
26442  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26443  *
26444  * @constructor
26445  * Run a new Table Filter
26446  * @param {Object} config Configuration options
26447  */
26448
26449 Roo.htmleditor.FilterTableWidth = function(cfg)
26450 {
26451     // no need to apply config.
26452     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26453     this.walk(cfg.node);
26454 }
26455
26456 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26457 {
26458      
26459      
26460     
26461     replaceTag: function(node) {
26462         
26463         
26464       
26465         if (node.hasAttribute('width')) {
26466             node.removeAttribute('width');
26467         }
26468         
26469          
26470         if (node.hasAttribute("style")) {
26471             // pretty basic...
26472             
26473             var styles = node.getAttribute("style").split(";");
26474             var nstyle = [];
26475             Roo.each(styles, function(s) {
26476                 if (!s.match(/:/)) {
26477                     return;
26478                 }
26479                 var kv = s.split(":");
26480                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26481                     return;
26482                 }
26483                 // what ever is left... we allow.
26484                 nstyle.push(s);
26485             });
26486             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26487             if (!nstyle.length) {
26488                 node.removeAttribute('style');
26489             }
26490         }
26491         
26492         return true; // continue doing children..
26493     }
26494 });/**
26495  * @class Roo.htmleditor.FilterWord
26496  * try and clean up all the mess that Word generates.
26497  * 
26498  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26499  
26500  * @constructor
26501  * Run a new Span Filter
26502  * @param {Object} config Configuration options
26503  */
26504
26505 Roo.htmleditor.FilterWord = function(cfg)
26506 {
26507     // no need to apply config.
26508     this.replaceDocBullets(cfg.node);
26509     
26510     this.replaceAname(cfg.node);
26511     // this is disabled as the removal is done by other filters;
26512    // this.walk(cfg.node);
26513     
26514     
26515 }
26516
26517 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26518 {
26519     tag: true,
26520      
26521     
26522     /**
26523      * Clean up MS wordisms...
26524      */
26525     replaceTag : function(node)
26526     {
26527          
26528         // no idea what this does - span with text, replaceds with just text.
26529         if(
26530                 node.nodeName == 'SPAN' &&
26531                 !node.hasAttributes() &&
26532                 node.childNodes.length == 1 &&
26533                 node.firstChild.nodeName == "#text"  
26534         ) {
26535             var textNode = node.firstChild;
26536             node.removeChild(textNode);
26537             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26538                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26539             }
26540             node.parentNode.insertBefore(textNode, node);
26541             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26542                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26543             }
26544             
26545             node.parentNode.removeChild(node);
26546             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26547         }
26548         
26549    
26550         
26551         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26552             node.parentNode.removeChild(node);
26553             return false; // dont do chidlren
26554         }
26555         //Roo.log(node.tagName);
26556         // remove - but keep children..
26557         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26558             //Roo.log('-- removed');
26559             while (node.childNodes.length) {
26560                 var cn = node.childNodes[0];
26561                 node.removeChild(cn);
26562                 node.parentNode.insertBefore(cn, node);
26563                 // move node to parent - and clean it..
26564                 if (cn.nodeType == 1) {
26565                     this.replaceTag(cn);
26566                 }
26567                 
26568             }
26569             node.parentNode.removeChild(node);
26570             /// no need to iterate chidlren = it's got none..
26571             //this.iterateChildren(node, this.cleanWord);
26572             return false; // no need to iterate children.
26573         }
26574         // clean styles
26575         if (node.className.length) {
26576             
26577             var cn = node.className.split(/\W+/);
26578             var cna = [];
26579             Roo.each(cn, function(cls) {
26580                 if (cls.match(/Mso[a-zA-Z]+/)) {
26581                     return;
26582                 }
26583                 cna.push(cls);
26584             });
26585             node.className = cna.length ? cna.join(' ') : '';
26586             if (!cna.length) {
26587                 node.removeAttribute("class");
26588             }
26589         }
26590         
26591         if (node.hasAttribute("lang")) {
26592             node.removeAttribute("lang");
26593         }
26594         
26595         if (node.hasAttribute("style")) {
26596             
26597             var styles = node.getAttribute("style").split(";");
26598             var nstyle = [];
26599             Roo.each(styles, function(s) {
26600                 if (!s.match(/:/)) {
26601                     return;
26602                 }
26603                 var kv = s.split(":");
26604                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26605                     return;
26606                 }
26607                 // what ever is left... we allow.
26608                 nstyle.push(s);
26609             });
26610             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26611             if (!nstyle.length) {
26612                 node.removeAttribute('style');
26613             }
26614         }
26615         return true; // do children
26616         
26617         
26618         
26619     },
26620     
26621     styleToObject: function(node)
26622     {
26623         var styles = (node.getAttribute("style") || '').split(";");
26624         var ret = {};
26625         Roo.each(styles, function(s) {
26626             if (!s.match(/:/)) {
26627                 return;
26628             }
26629             var kv = s.split(":");
26630              
26631             // what ever is left... we allow.
26632             ret[kv[0].trim()] = kv[1];
26633         });
26634         return ret;
26635     },
26636     
26637     
26638     replaceAname : function (doc)
26639     {
26640         // replace all the a/name without..
26641         var aa = Array.from(doc.getElementsByTagName('a'));
26642         for (var i = 0; i  < aa.length; i++) {
26643             var a = aa[i];
26644             if (a.hasAttribute("name")) {
26645                 a.removeAttribute("name");
26646             }
26647             if (a.hasAttribute("href")) {
26648                 continue;
26649             }
26650             // reparent children.
26651             this.removeNodeKeepChildren(a);
26652             
26653         }
26654         
26655         
26656         
26657     },
26658
26659     
26660     
26661     replaceDocBullets : function(doc)
26662     {
26663         // this is a bit odd - but it appears some indents use ql-indent-1
26664          //Roo.log(doc.innerHTML);
26665         
26666         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26667         for( var i = 0; i < listpara.length; i ++) {
26668             listpara[i].className = "MsoListParagraph";
26669         }
26670         
26671         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26672         for( var i = 0; i < listpara.length; i ++) {
26673             listpara[i].className = "MsoListParagraph";
26674         }
26675         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26676         for( var i = 0; i < listpara.length; i ++) {
26677             listpara[i].className = "MsoListParagraph";
26678         }
26679         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26680         for( var i = 0; i < listpara.length; i ++) {
26681             listpara[i].className = "MsoListParagraph";
26682         }
26683         
26684         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26685         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26686         for( var i = 0; i < htwo.length; i ++) {
26687             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26688                 htwo[i].className = "MsoListParagraph";
26689             }
26690         }
26691         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26692         for( var i = 0; i < listpara.length; i ++) {
26693             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26694                 listpara[i].className = "MsoListParagraph";
26695             } else {
26696                 listpara[i].className = "MsoNormalx";
26697             }
26698         }
26699        
26700         listpara = doc.getElementsByClassName('MsoListParagraph');
26701         // Roo.log(doc.innerHTML);
26702         
26703         
26704         
26705         while(listpara.length) {
26706             
26707             this.replaceDocBullet(listpara.item(0));
26708         }
26709       
26710     },
26711     
26712      
26713     
26714     replaceDocBullet : function(p)
26715     {
26716         // gather all the siblings.
26717         var ns = p,
26718             parent = p.parentNode,
26719             doc = parent.ownerDocument,
26720             items = [];
26721             
26722         var listtype = 'ul';   
26723         while (ns) {
26724             if (ns.nodeType != 1) {
26725                 ns = ns.nextSibling;
26726                 continue;
26727             }
26728             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26729                 break;
26730             }
26731             var spans = ns.getElementsByTagName('span');
26732             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26733                 items.push(ns);
26734                 ns = ns.nextSibling;
26735                 has_list = true;
26736                 if (spans.length && spans[0].hasAttribute('style')) {
26737                     var  style = this.styleToObject(spans[0]);
26738                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26739                         listtype = 'ol';
26740                     }
26741                 }
26742                 
26743                 continue;
26744             }
26745             var spans = ns.getElementsByTagName('span');
26746             if (!spans.length) {
26747                 break;
26748             }
26749             var has_list  = false;
26750             for(var i = 0; i < spans.length; i++) {
26751                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26752                     has_list = true;
26753                     break;
26754                 }
26755             }
26756             if (!has_list) {
26757                 break;
26758             }
26759             items.push(ns);
26760             ns = ns.nextSibling;
26761             
26762             
26763         }
26764         if (!items.length) {
26765             ns.className = "";
26766             return;
26767         }
26768         
26769         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26770         parent.insertBefore(ul, p);
26771         var lvl = 0;
26772         var stack = [ ul ];
26773         var last_li = false;
26774         
26775         var margin_to_depth = {};
26776         max_margins = -1;
26777         
26778         items.forEach(function(n, ipos) {
26779             //Roo.log("got innertHMLT=" + n.innerHTML);
26780             
26781             var spans = n.getElementsByTagName('span');
26782             if (!spans.length) {
26783                 //Roo.log("No spans found");
26784                  
26785                 parent.removeChild(n);
26786                 
26787                 
26788                 return; // skip it...
26789             }
26790            
26791                 
26792             var num = 1;
26793             var style = {};
26794             for(var i = 0; i < spans.length; i++) {
26795             
26796                 style = this.styleToObject(spans[i]);
26797                 if (typeof(style['mso-list']) == 'undefined') {
26798                     continue;
26799                 }
26800                 if (listtype == 'ol') {
26801                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26802                 }
26803                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26804                 break;
26805             }
26806             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26807             style = this.styleToObject(n); // mo-list is from the parent node.
26808             if (typeof(style['mso-list']) == 'undefined') {
26809                 //Roo.log("parent is missing level");
26810                   
26811                 parent.removeChild(n);
26812                  
26813                 return;
26814             }
26815             
26816             var margin = style['margin-left'];
26817             if (typeof(margin_to_depth[margin]) == 'undefined') {
26818                 max_margins++;
26819                 margin_to_depth[margin] = max_margins;
26820             }
26821             nlvl = margin_to_depth[margin] ;
26822              
26823             if (nlvl > lvl) {
26824                 //new indent
26825                 var nul = doc.createElement(listtype); // what about number lists...
26826                 if (!last_li) {
26827                     last_li = doc.createElement('li');
26828                     stack[lvl].appendChild(last_li);
26829                 }
26830                 last_li.appendChild(nul);
26831                 stack[nlvl] = nul;
26832                 
26833             }
26834             lvl = nlvl;
26835             
26836             // not starting at 1..
26837             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26838                 stack[nlvl].setAttribute("start", num);
26839             }
26840             
26841             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26842             last_li = nli;
26843             nli.innerHTML = n.innerHTML;
26844             //Roo.log("innerHTML = " + n.innerHTML);
26845             parent.removeChild(n);
26846             
26847              
26848              
26849             
26850         },this);
26851         
26852         
26853         
26854         
26855     }
26856     
26857     
26858     
26859 });
26860 /**
26861  * @class Roo.htmleditor.FilterStyleToTag
26862  * part of the word stuff... - certain 'styles' should be converted to tags.
26863  * eg.
26864  *   font-weight: bold -> bold
26865  *   ?? super / subscrit etc..
26866  * 
26867  * @constructor
26868 * Run a new style to tag filter.
26869 * @param {Object} config Configuration options
26870  */
26871 Roo.htmleditor.FilterStyleToTag = function(cfg)
26872 {
26873     
26874     this.tags = {
26875         B  : [ 'fontWeight' , 'bold'],
26876         I :  [ 'fontStyle' , 'italic'],
26877         //pre :  [ 'font-style' , 'italic'],
26878         // h1.. h6 ?? font-size?
26879         SUP : [ 'verticalAlign' , 'super' ],
26880         SUB : [ 'verticalAlign' , 'sub' ]
26881         
26882         
26883     };
26884     
26885     Roo.apply(this, cfg);
26886      
26887     
26888     this.walk(cfg.node);
26889     
26890     
26891     
26892 }
26893
26894
26895 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26896 {
26897     tag: true, // all tags
26898     
26899     tags : false,
26900     
26901     
26902     replaceTag : function(node)
26903     {
26904         
26905         
26906         if (node.getAttribute("style") === null) {
26907             return true;
26908         }
26909         var inject = [];
26910         for (var k in this.tags) {
26911             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26912                 inject.push(k);
26913                 node.style.removeProperty(this.tags[k][0]);
26914             }
26915         }
26916         if (!inject.length) {
26917             return true; 
26918         }
26919         var cn = Array.from(node.childNodes);
26920         var nn = node;
26921         Roo.each(inject, function(t) {
26922             var nc = node.ownerDocument.createElement(t);
26923             nn.appendChild(nc);
26924             nn = nc;
26925         });
26926         for(var i = 0;i < cn.length;cn++) {
26927             node.removeChild(cn[i]);
26928             nn.appendChild(cn[i]);
26929         }
26930         return true /// iterate thru
26931     }
26932     
26933 })/**
26934  * @class Roo.htmleditor.FilterLongBr
26935  * BR/BR/BR - keep a maximum of 2...
26936  * @constructor
26937  * Run a new Long BR Filter
26938  * @param {Object} config Configuration options
26939  */
26940
26941 Roo.htmleditor.FilterLongBr = function(cfg)
26942 {
26943     // no need to apply config.
26944     this.walk(cfg.node);
26945 }
26946
26947 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26948 {
26949     
26950      
26951     tag : 'BR',
26952     
26953      
26954     replaceTag : function(node)
26955     {
26956         
26957         var ps = node.nextSibling;
26958         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26959             ps = ps.nextSibling;
26960         }
26961         
26962         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26963             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26964             return false;
26965         }
26966         
26967         if (!ps || ps.nodeType != 1) {
26968             return false;
26969         }
26970         
26971         if (!ps || ps.tagName != 'BR') {
26972            
26973             return false;
26974         }
26975         
26976         
26977         
26978         
26979         
26980         if (!node.previousSibling) {
26981             return false;
26982         }
26983         var ps = node.previousSibling;
26984         
26985         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26986             ps = ps.previousSibling;
26987         }
26988         if (!ps || ps.nodeType != 1) {
26989             return false;
26990         }
26991         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26992         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26993             return false;
26994         }
26995         
26996         node.parentNode.removeChild(node); // remove me...
26997         
26998         return false; // no need to do children
26999
27000     }
27001     
27002 }); 
27003
27004 /**
27005  * @class Roo.htmleditor.FilterBlock
27006  * removes id / data-block and contenteditable that are associated with blocks
27007  * usage should be done on a cloned copy of the dom
27008  * @constructor
27009 * Run a new Attribute Filter { node : xxxx }}
27010 * @param {Object} config Configuration options
27011  */
27012 Roo.htmleditor.FilterBlock = function(cfg)
27013 {
27014     Roo.apply(this, cfg);
27015     var qa = cfg.node.querySelectorAll;
27016     this.removeAttributes('data-block');
27017     this.removeAttributes('contenteditable');
27018     this.removeAttributes('id');
27019     
27020 }
27021
27022 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27023 {
27024     node: true, // all tags
27025      
27026      
27027     removeAttributes : function(attr)
27028     {
27029         var ar = this.node.querySelectorAll('*[' + attr + ']');
27030         for (var i =0;i<ar.length;i++) {
27031             ar[i].removeAttribute(attr);
27032         }
27033     }
27034         
27035         
27036         
27037     
27038 });
27039 /***
27040  * This is based loosely on tinymce 
27041  * @class Roo.htmleditor.TidySerializer
27042  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27043  * @constructor
27044  * @method Serializer
27045  * @param {Object} settings Name/value settings object.
27046  */
27047
27048
27049 Roo.htmleditor.TidySerializer = function(settings)
27050 {
27051     Roo.apply(this, settings);
27052     
27053     this.writer = new Roo.htmleditor.TidyWriter(settings);
27054     
27055     
27056
27057 };
27058 Roo.htmleditor.TidySerializer.prototype = {
27059     
27060     /**
27061      * @param {boolean} inner do the inner of the node.
27062      */
27063     inner : false,
27064     
27065     writer : false,
27066     
27067     /**
27068     * Serializes the specified node into a string.
27069     *
27070     * @example
27071     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27072     * @method serialize
27073     * @param {DomElement} node Node instance to serialize.
27074     * @return {String} String with HTML based on DOM tree.
27075     */
27076     serialize : function(node) {
27077         
27078         // = settings.validate;
27079         var writer = this.writer;
27080         var self  = this;
27081         this.handlers = {
27082             // #text
27083             3: function(node) {
27084                 
27085                 writer.text(node.nodeValue, node);
27086             },
27087             // #comment
27088             8: function(node) {
27089                 writer.comment(node.nodeValue);
27090             },
27091             // Processing instruction
27092             7: function(node) {
27093                 writer.pi(node.name, node.nodeValue);
27094             },
27095             // Doctype
27096             10: function(node) {
27097                 writer.doctype(node.nodeValue);
27098             },
27099             // CDATA
27100             4: function(node) {
27101                 writer.cdata(node.nodeValue);
27102             },
27103             // Document fragment
27104             11: function(node) {
27105                 node = node.firstChild;
27106                 if (!node) {
27107                     return;
27108                 }
27109                 while(node) {
27110                     self.walk(node);
27111                     node = node.nextSibling
27112                 }
27113             }
27114         };
27115         writer.reset();
27116         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27117         return writer.getContent();
27118     },
27119
27120     walk: function(node)
27121     {
27122         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27123             handler = this.handlers[node.nodeType];
27124             
27125         if (handler) {
27126             handler(node);
27127             return;
27128         }
27129     
27130         var name = node.nodeName;
27131         var isEmpty = node.childNodes.length < 1;
27132       
27133         var writer = this.writer;
27134         var attrs = node.attributes;
27135         // Sort attributes
27136         
27137         writer.start(node.nodeName, attrs, isEmpty, node);
27138         if (isEmpty) {
27139             return;
27140         }
27141         node = node.firstChild;
27142         if (!node) {
27143             writer.end(name);
27144             return;
27145         }
27146         while (node) {
27147             this.walk(node);
27148             node = node.nextSibling;
27149         }
27150         writer.end(name);
27151         
27152     
27153     }
27154     // Serialize element and treat all non elements as fragments
27155    
27156 }; 
27157
27158 /***
27159  * This is based loosely on tinymce 
27160  * @class Roo.htmleditor.TidyWriter
27161  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27162  *
27163  * Known issues?
27164  * - not tested much with 'PRE' formated elements.
27165  * 
27166  *
27167  *
27168  */
27169
27170 Roo.htmleditor.TidyWriter = function(settings)
27171 {
27172     
27173     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27174     Roo.apply(this, settings);
27175     this.html = [];
27176     this.state = [];
27177      
27178     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27179   
27180 }
27181 Roo.htmleditor.TidyWriter.prototype = {
27182
27183  
27184     state : false,
27185     
27186     indent :  '  ',
27187     
27188     // part of state...
27189     indentstr : '',
27190     in_pre: false,
27191     in_inline : false,
27192     last_inline : false,
27193     encode : false,
27194      
27195     
27196             /**
27197     * Writes the a start element such as <p id="a">.
27198     *
27199     * @method start
27200     * @param {String} name Name of the element.
27201     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27202     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27203     */
27204     start: function(name, attrs, empty, node)
27205     {
27206         var i, l, attr, value;
27207         
27208         // there are some situations where adding line break && indentation will not work. will not work.
27209         // <span / b / i ... formating?
27210         
27211         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27212         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27213         
27214         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27215         
27216         var add_lb = name == 'BR' ? false : in_inline;
27217         
27218         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27219             i_inline = false;
27220         }
27221
27222         var indentstr =  this.indentstr;
27223         
27224         // e_inline = elements that can be inline, but still allow \n before and after?
27225         // only 'BR' ??? any others?
27226         
27227         // ADD LINE BEFORE tage
27228         if (!this.in_pre) {
27229             if (in_inline) {
27230                 //code
27231                 if (name == 'BR') {
27232                     this.addLine();
27233                 } else if (this.lastElementEndsWS()) {
27234                     this.addLine();
27235                 } else{
27236                     // otherwise - no new line. (and dont indent.)
27237                     indentstr = '';
27238                 }
27239                 
27240             } else {
27241                 this.addLine();
27242             }
27243         } else {
27244             indentstr = '';
27245         }
27246         
27247         this.html.push(indentstr + '<', name.toLowerCase());
27248         
27249         if (attrs) {
27250             for (i = 0, l = attrs.length; i < l; i++) {
27251                 attr = attrs[i];
27252                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27253             }
27254         }
27255      
27256         if (empty) {
27257             if (is_short) {
27258                 this.html[this.html.length] = '/>';
27259             } else {
27260                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27261             }
27262             var e_inline = name == 'BR' ? false : this.in_inline;
27263             
27264             if (!e_inline && !this.in_pre) {
27265                 this.addLine();
27266             }
27267             return;
27268         
27269         }
27270         // not empty..
27271         this.html[this.html.length] = '>';
27272         
27273         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27274         /*
27275         if (!in_inline && !in_pre) {
27276             var cn = node.firstChild;
27277             while(cn) {
27278                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27279                     in_inline = true
27280                     break;
27281                 }
27282                 cn = cn.nextSibling;
27283             }
27284              
27285         }
27286         */
27287         
27288         
27289         this.pushState({
27290             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27291             in_pre : in_pre,
27292             in_inline :  in_inline
27293         });
27294         // add a line after if we are not in a
27295         
27296         if (!in_inline && !in_pre) {
27297             this.addLine();
27298         }
27299         
27300             
27301          
27302         
27303     },
27304     
27305     lastElementEndsWS : function()
27306     {
27307         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27308         if (value === false) {
27309             return true;
27310         }
27311         return value.match(/\s+$/);
27312         
27313     },
27314     
27315     /**
27316      * Writes the a end element such as </p>.
27317      *
27318      * @method end
27319      * @param {String} name Name of the element.
27320      */
27321     end: function(name) {
27322         var value;
27323         this.popState();
27324         var indentstr = '';
27325         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27326         
27327         if (!this.in_pre && !in_inline) {
27328             this.addLine();
27329             indentstr  = this.indentstr;
27330         }
27331         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27332         this.last_inline = in_inline;
27333         
27334         // pop the indent state..
27335     },
27336     /**
27337      * Writes a text node.
27338      *
27339      * In pre - we should not mess with the contents.
27340      * 
27341      *
27342      * @method text
27343      * @param {String} text String to write out.
27344      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27345      */
27346     text: function(in_text, node)
27347     {
27348         // if not in whitespace critical
27349         if (in_text.length < 1) {
27350             return;
27351         }
27352         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27353         
27354         if (this.in_pre) {
27355             this.html[this.html.length] =  text;
27356             return;   
27357         }
27358         
27359         if (this.in_inline) {
27360             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27361             if (text != ' ') {
27362                 text = text.replace(/\s+/,' ');  // all white space to single white space
27363                 
27364                     
27365                 // if next tag is '<BR>', then we can trim right..
27366                 if (node.nextSibling &&
27367                     node.nextSibling.nodeType == 1 &&
27368                     node.nextSibling.nodeName == 'BR' )
27369                 {
27370                     text = text.replace(/\s+$/g,'');
27371                 }
27372                 // if previous tag was a BR, we can also trim..
27373                 if (node.previousSibling &&
27374                     node.previousSibling.nodeType == 1 &&
27375                     node.previousSibling.nodeName == 'BR' )
27376                 {
27377                     text = this.indentstr +  text.replace(/^\s+/g,'');
27378                 }
27379                 if (text.match(/\n/)) {
27380                     text = text.replace(
27381                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27382                     );
27383                     // remoeve the last whitespace / line break.
27384                     text = text.replace(/\n\s+$/,'');
27385                 }
27386                 // repace long lines
27387                 
27388             }
27389              
27390             this.html[this.html.length] =  text;
27391             return;   
27392         }
27393         // see if previous element was a inline element.
27394         var indentstr = this.indentstr;
27395    
27396         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27397         
27398         // should trim left?
27399         if (node.previousSibling &&
27400             node.previousSibling.nodeType == 1 &&
27401             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27402         {
27403             indentstr = '';
27404             
27405         } else {
27406             this.addLine();
27407             text = text.replace(/^\s+/,''); // trim left
27408           
27409         }
27410         // should trim right?
27411         if (node.nextSibling &&
27412             node.nextSibling.nodeType == 1 &&
27413             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27414         {
27415           // noop
27416             
27417         }  else {
27418             text = text.replace(/\s+$/,''); // trim right
27419         }
27420          
27421               
27422         
27423         
27424         
27425         if (text.length < 1) {
27426             return;
27427         }
27428         if (!text.match(/\n/)) {
27429             this.html.push(indentstr + text);
27430             return;
27431         }
27432         
27433         text = this.indentstr + text.replace(
27434             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27435         );
27436         // remoeve the last whitespace / line break.
27437         text = text.replace(/\s+$/,''); 
27438         
27439         this.html.push(text);
27440         
27441         // split and indent..
27442         
27443         
27444     },
27445     /**
27446      * Writes a cdata node such as <![CDATA[data]]>.
27447      *
27448      * @method cdata
27449      * @param {String} text String to write out inside the cdata.
27450      */
27451     cdata: function(text) {
27452         this.html.push('<![CDATA[', text, ']]>');
27453     },
27454     /**
27455     * Writes a comment node such as <!-- Comment -->.
27456     *
27457     * @method cdata
27458     * @param {String} text String to write out inside the comment.
27459     */
27460    comment: function(text) {
27461        this.html.push('<!--', text, '-->');
27462    },
27463     /**
27464      * Writes a PI node such as <?xml attr="value" ?>.
27465      *
27466      * @method pi
27467      * @param {String} name Name of the pi.
27468      * @param {String} text String to write out inside the pi.
27469      */
27470     pi: function(name, text) {
27471         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27472         this.indent != '' && this.html.push('\n');
27473     },
27474     /**
27475      * Writes a doctype node such as <!DOCTYPE data>.
27476      *
27477      * @method doctype
27478      * @param {String} text String to write out inside the doctype.
27479      */
27480     doctype: function(text) {
27481         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27482     },
27483     /**
27484      * Resets the internal buffer if one wants to reuse the writer.
27485      *
27486      * @method reset
27487      */
27488     reset: function() {
27489         this.html.length = 0;
27490         this.state = [];
27491         this.pushState({
27492             indentstr : '',
27493             in_pre : false, 
27494             in_inline : false
27495         })
27496     },
27497     /**
27498      * Returns the contents that got serialized.
27499      *
27500      * @method getContent
27501      * @return {String} HTML contents that got written down.
27502      */
27503     getContent: function() {
27504         return this.html.join('').replace(/\n$/, '');
27505     },
27506     
27507     pushState : function(cfg)
27508     {
27509         this.state.push(cfg);
27510         Roo.apply(this, cfg);
27511     },
27512     
27513     popState : function()
27514     {
27515         if (this.state.length < 1) {
27516             return; // nothing to push
27517         }
27518         var cfg = {
27519             in_pre: false,
27520             indentstr : ''
27521         };
27522         this.state.pop();
27523         if (this.state.length > 0) {
27524             cfg = this.state[this.state.length-1]; 
27525         }
27526         Roo.apply(this, cfg);
27527     },
27528     
27529     addLine: function()
27530     {
27531         if (this.html.length < 1) {
27532             return;
27533         }
27534         
27535         
27536         var value = this.html[this.html.length - 1];
27537         if (value.length > 0 && '\n' !== value) {
27538             this.html.push('\n');
27539         }
27540     }
27541     
27542     
27543 //'pre script noscript style textarea video audio iframe object code'
27544 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
27545 // inline 
27546 };
27547
27548 Roo.htmleditor.TidyWriter.inline_elements = [
27549         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
27550         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
27551 ];
27552 Roo.htmleditor.TidyWriter.shortend_elements = [
27553     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
27554     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
27555 ];
27556
27557 Roo.htmleditor.TidyWriter.whitespace_elements = [
27558     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
27559 ];/***
27560  * This is based loosely on tinymce 
27561  * @class Roo.htmleditor.TidyEntities
27562  * @static
27563  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27564  *
27565  * Not 100% sure this is actually used or needed.
27566  */
27567
27568 Roo.htmleditor.TidyEntities = {
27569     
27570     /**
27571      * initialize data..
27572      */
27573     init : function (){
27574      
27575         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
27576        
27577     },
27578
27579
27580     buildEntitiesLookup: function(items, radix) {
27581         var i, chr, entity, lookup = {};
27582         if (!items) {
27583             return {};
27584         }
27585         items = typeof(items) == 'string' ? items.split(',') : items;
27586         radix = radix || 10;
27587         // Build entities lookup table
27588         for (i = 0; i < items.length; i += 2) {
27589             chr = String.fromCharCode(parseInt(items[i], radix));
27590             // Only add non base entities
27591             if (!this.baseEntities[chr]) {
27592                 entity = '&' + items[i + 1] + ';';
27593                 lookup[chr] = entity;
27594                 lookup[entity] = chr;
27595             }
27596         }
27597         return lookup;
27598         
27599     },
27600     
27601     asciiMap : {
27602             128: '€',
27603             130: '‚',
27604             131: 'ƒ',
27605             132: '„',
27606             133: '…',
27607             134: '†',
27608             135: '‡',
27609             136: 'ˆ',
27610             137: '‰',
27611             138: 'Š',
27612             139: '‹',
27613             140: 'Œ',
27614             142: 'Ž',
27615             145: '‘',
27616             146: '’',
27617             147: '“',
27618             148: '”',
27619             149: '•',
27620             150: '–',
27621             151: '—',
27622             152: '˜',
27623             153: '™',
27624             154: 'š',
27625             155: '›',
27626             156: 'œ',
27627             158: 'ž',
27628             159: 'Ÿ'
27629     },
27630     // Raw entities
27631     baseEntities : {
27632         '"': '&quot;',
27633         // Needs to be escaped since the YUI compressor would otherwise break the code
27634         '\'': '&#39;',
27635         '<': '&lt;',
27636         '>': '&gt;',
27637         '&': '&amp;',
27638         '`': '&#96;'
27639     },
27640     // Reverse lookup table for raw entities
27641     reverseEntities : {
27642         '&lt;': '<',
27643         '&gt;': '>',
27644         '&amp;': '&',
27645         '&quot;': '"',
27646         '&apos;': '\''
27647     },
27648     
27649     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27650     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27651     rawCharsRegExp : /[<>&\"\']/g,
27652     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
27653     namedEntities  : false,
27654     namedEntitiesData : [ 
27655         '50',
27656         'nbsp',
27657         '51',
27658         'iexcl',
27659         '52',
27660         'cent',
27661         '53',
27662         'pound',
27663         '54',
27664         'curren',
27665         '55',
27666         'yen',
27667         '56',
27668         'brvbar',
27669         '57',
27670         'sect',
27671         '58',
27672         'uml',
27673         '59',
27674         'copy',
27675         '5a',
27676         'ordf',
27677         '5b',
27678         'laquo',
27679         '5c',
27680         'not',
27681         '5d',
27682         'shy',
27683         '5e',
27684         'reg',
27685         '5f',
27686         'macr',
27687         '5g',
27688         'deg',
27689         '5h',
27690         'plusmn',
27691         '5i',
27692         'sup2',
27693         '5j',
27694         'sup3',
27695         '5k',
27696         'acute',
27697         '5l',
27698         'micro',
27699         '5m',
27700         'para',
27701         '5n',
27702         'middot',
27703         '5o',
27704         'cedil',
27705         '5p',
27706         'sup1',
27707         '5q',
27708         'ordm',
27709         '5r',
27710         'raquo',
27711         '5s',
27712         'frac14',
27713         '5t',
27714         'frac12',
27715         '5u',
27716         'frac34',
27717         '5v',
27718         'iquest',
27719         '60',
27720         'Agrave',
27721         '61',
27722         'Aacute',
27723         '62',
27724         'Acirc',
27725         '63',
27726         'Atilde',
27727         '64',
27728         'Auml',
27729         '65',
27730         'Aring',
27731         '66',
27732         'AElig',
27733         '67',
27734         'Ccedil',
27735         '68',
27736         'Egrave',
27737         '69',
27738         'Eacute',
27739         '6a',
27740         'Ecirc',
27741         '6b',
27742         'Euml',
27743         '6c',
27744         'Igrave',
27745         '6d',
27746         'Iacute',
27747         '6e',
27748         'Icirc',
27749         '6f',
27750         'Iuml',
27751         '6g',
27752         'ETH',
27753         '6h',
27754         'Ntilde',
27755         '6i',
27756         'Ograve',
27757         '6j',
27758         'Oacute',
27759         '6k',
27760         'Ocirc',
27761         '6l',
27762         'Otilde',
27763         '6m',
27764         'Ouml',
27765         '6n',
27766         'times',
27767         '6o',
27768         'Oslash',
27769         '6p',
27770         'Ugrave',
27771         '6q',
27772         'Uacute',
27773         '6r',
27774         'Ucirc',
27775         '6s',
27776         'Uuml',
27777         '6t',
27778         'Yacute',
27779         '6u',
27780         'THORN',
27781         '6v',
27782         'szlig',
27783         '70',
27784         'agrave',
27785         '71',
27786         'aacute',
27787         '72',
27788         'acirc',
27789         '73',
27790         'atilde',
27791         '74',
27792         'auml',
27793         '75',
27794         'aring',
27795         '76',
27796         'aelig',
27797         '77',
27798         'ccedil',
27799         '78',
27800         'egrave',
27801         '79',
27802         'eacute',
27803         '7a',
27804         'ecirc',
27805         '7b',
27806         'euml',
27807         '7c',
27808         'igrave',
27809         '7d',
27810         'iacute',
27811         '7e',
27812         'icirc',
27813         '7f',
27814         'iuml',
27815         '7g',
27816         'eth',
27817         '7h',
27818         'ntilde',
27819         '7i',
27820         'ograve',
27821         '7j',
27822         'oacute',
27823         '7k',
27824         'ocirc',
27825         '7l',
27826         'otilde',
27827         '7m',
27828         'ouml',
27829         '7n',
27830         'divide',
27831         '7o',
27832         'oslash',
27833         '7p',
27834         'ugrave',
27835         '7q',
27836         'uacute',
27837         '7r',
27838         'ucirc',
27839         '7s',
27840         'uuml',
27841         '7t',
27842         'yacute',
27843         '7u',
27844         'thorn',
27845         '7v',
27846         'yuml',
27847         'ci',
27848         'fnof',
27849         'sh',
27850         'Alpha',
27851         'si',
27852         'Beta',
27853         'sj',
27854         'Gamma',
27855         'sk',
27856         'Delta',
27857         'sl',
27858         'Epsilon',
27859         'sm',
27860         'Zeta',
27861         'sn',
27862         'Eta',
27863         'so',
27864         'Theta',
27865         'sp',
27866         'Iota',
27867         'sq',
27868         'Kappa',
27869         'sr',
27870         'Lambda',
27871         'ss',
27872         'Mu',
27873         'st',
27874         'Nu',
27875         'su',
27876         'Xi',
27877         'sv',
27878         'Omicron',
27879         't0',
27880         'Pi',
27881         't1',
27882         'Rho',
27883         't3',
27884         'Sigma',
27885         't4',
27886         'Tau',
27887         't5',
27888         'Upsilon',
27889         't6',
27890         'Phi',
27891         't7',
27892         'Chi',
27893         't8',
27894         'Psi',
27895         't9',
27896         'Omega',
27897         'th',
27898         'alpha',
27899         'ti',
27900         'beta',
27901         'tj',
27902         'gamma',
27903         'tk',
27904         'delta',
27905         'tl',
27906         'epsilon',
27907         'tm',
27908         'zeta',
27909         'tn',
27910         'eta',
27911         'to',
27912         'theta',
27913         'tp',
27914         'iota',
27915         'tq',
27916         'kappa',
27917         'tr',
27918         'lambda',
27919         'ts',
27920         'mu',
27921         'tt',
27922         'nu',
27923         'tu',
27924         'xi',
27925         'tv',
27926         'omicron',
27927         'u0',
27928         'pi',
27929         'u1',
27930         'rho',
27931         'u2',
27932         'sigmaf',
27933         'u3',
27934         'sigma',
27935         'u4',
27936         'tau',
27937         'u5',
27938         'upsilon',
27939         'u6',
27940         'phi',
27941         'u7',
27942         'chi',
27943         'u8',
27944         'psi',
27945         'u9',
27946         'omega',
27947         'uh',
27948         'thetasym',
27949         'ui',
27950         'upsih',
27951         'um',
27952         'piv',
27953         '812',
27954         'bull',
27955         '816',
27956         'hellip',
27957         '81i',
27958         'prime',
27959         '81j',
27960         'Prime',
27961         '81u',
27962         'oline',
27963         '824',
27964         'frasl',
27965         '88o',
27966         'weierp',
27967         '88h',
27968         'image',
27969         '88s',
27970         'real',
27971         '892',
27972         'trade',
27973         '89l',
27974         'alefsym',
27975         '8cg',
27976         'larr',
27977         '8ch',
27978         'uarr',
27979         '8ci',
27980         'rarr',
27981         '8cj',
27982         'darr',
27983         '8ck',
27984         'harr',
27985         '8dl',
27986         'crarr',
27987         '8eg',
27988         'lArr',
27989         '8eh',
27990         'uArr',
27991         '8ei',
27992         'rArr',
27993         '8ej',
27994         'dArr',
27995         '8ek',
27996         'hArr',
27997         '8g0',
27998         'forall',
27999         '8g2',
28000         'part',
28001         '8g3',
28002         'exist',
28003         '8g5',
28004         'empty',
28005         '8g7',
28006         'nabla',
28007         '8g8',
28008         'isin',
28009         '8g9',
28010         'notin',
28011         '8gb',
28012         'ni',
28013         '8gf',
28014         'prod',
28015         '8gh',
28016         'sum',
28017         '8gi',
28018         'minus',
28019         '8gn',
28020         'lowast',
28021         '8gq',
28022         'radic',
28023         '8gt',
28024         'prop',
28025         '8gu',
28026         'infin',
28027         '8h0',
28028         'ang',
28029         '8h7',
28030         'and',
28031         '8h8',
28032         'or',
28033         '8h9',
28034         'cap',
28035         '8ha',
28036         'cup',
28037         '8hb',
28038         'int',
28039         '8hk',
28040         'there4',
28041         '8hs',
28042         'sim',
28043         '8i5',
28044         'cong',
28045         '8i8',
28046         'asymp',
28047         '8j0',
28048         'ne',
28049         '8j1',
28050         'equiv',
28051         '8j4',
28052         'le',
28053         '8j5',
28054         'ge',
28055         '8k2',
28056         'sub',
28057         '8k3',
28058         'sup',
28059         '8k4',
28060         'nsub',
28061         '8k6',
28062         'sube',
28063         '8k7',
28064         'supe',
28065         '8kl',
28066         'oplus',
28067         '8kn',
28068         'otimes',
28069         '8l5',
28070         'perp',
28071         '8m5',
28072         'sdot',
28073         '8o8',
28074         'lceil',
28075         '8o9',
28076         'rceil',
28077         '8oa',
28078         'lfloor',
28079         '8ob',
28080         'rfloor',
28081         '8p9',
28082         'lang',
28083         '8pa',
28084         'rang',
28085         '9ea',
28086         'loz',
28087         '9j0',
28088         'spades',
28089         '9j3',
28090         'clubs',
28091         '9j5',
28092         'hearts',
28093         '9j6',
28094         'diams',
28095         'ai',
28096         'OElig',
28097         'aj',
28098         'oelig',
28099         'b0',
28100         'Scaron',
28101         'b1',
28102         'scaron',
28103         'bo',
28104         'Yuml',
28105         'm6',
28106         'circ',
28107         'ms',
28108         'tilde',
28109         '802',
28110         'ensp',
28111         '803',
28112         'emsp',
28113         '809',
28114         'thinsp',
28115         '80c',
28116         'zwnj',
28117         '80d',
28118         'zwj',
28119         '80e',
28120         'lrm',
28121         '80f',
28122         'rlm',
28123         '80j',
28124         'ndash',
28125         '80k',
28126         'mdash',
28127         '80o',
28128         'lsquo',
28129         '80p',
28130         'rsquo',
28131         '80q',
28132         'sbquo',
28133         '80s',
28134         'ldquo',
28135         '80t',
28136         'rdquo',
28137         '80u',
28138         'bdquo',
28139         '810',
28140         'dagger',
28141         '811',
28142         'Dagger',
28143         '81g',
28144         'permil',
28145         '81p',
28146         'lsaquo',
28147         '81q',
28148         'rsaquo',
28149         '85c',
28150         'euro'
28151     ],
28152
28153          
28154     /**
28155      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28156      *
28157      * @method encodeRaw
28158      * @param {String} text Text to encode.
28159      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28160      * @return {String} Entity encoded text.
28161      */
28162     encodeRaw: function(text, attr)
28163     {
28164         var t = this;
28165         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28166             return t.baseEntities[chr] || chr;
28167         });
28168     },
28169     /**
28170      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28171      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28172      * and is exposed as the DOMUtils.encode function.
28173      *
28174      * @method encodeAllRaw
28175      * @param {String} text Text to encode.
28176      * @return {String} Entity encoded text.
28177      */
28178     encodeAllRaw: function(text) {
28179         var t = this;
28180         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28181             return t.baseEntities[chr] || chr;
28182         });
28183     },
28184     /**
28185      * Encodes the specified string using numeric entities. The core entities will be
28186      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28187      *
28188      * @method encodeNumeric
28189      * @param {String} text Text to encode.
28190      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28191      * @return {String} Entity encoded text.
28192      */
28193     encodeNumeric: function(text, attr) {
28194         var t = this;
28195         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28196             // Multi byte sequence convert it to a single entity
28197             if (chr.length > 1) {
28198                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28199             }
28200             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28201         });
28202     },
28203     /**
28204      * Encodes the specified string using named entities. The core entities will be encoded
28205      * as named ones but all non lower ascii characters will be encoded into named entities.
28206      *
28207      * @method encodeNamed
28208      * @param {String} text Text to encode.
28209      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28210      * @param {Object} entities Optional parameter with entities to use.
28211      * @return {String} Entity encoded text.
28212      */
28213     encodeNamed: function(text, attr, entities) {
28214         var t = this;
28215         entities = entities || this.namedEntities;
28216         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28217             return t.baseEntities[chr] || entities[chr] || chr;
28218         });
28219     },
28220     /**
28221      * Returns an encode function based on the name(s) and it's optional entities.
28222      *
28223      * @method getEncodeFunc
28224      * @param {String} name Comma separated list of encoders for example named,numeric.
28225      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28226      * @return {function} Encode function to be used.
28227      */
28228     getEncodeFunc: function(name, entities) {
28229         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28230         var t = this;
28231         function encodeNamedAndNumeric(text, attr) {
28232             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28233                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28234             });
28235         }
28236
28237         function encodeCustomNamed(text, attr) {
28238             return t.encodeNamed(text, attr, entities);
28239         }
28240         // Replace + with , to be compatible with previous TinyMCE versions
28241         name = this.makeMap(name.replace(/\+/g, ','));
28242         // Named and numeric encoder
28243         if (name.named && name.numeric) {
28244             return this.encodeNamedAndNumeric;
28245         }
28246         // Named encoder
28247         if (name.named) {
28248             // Custom names
28249             if (entities) {
28250                 return encodeCustomNamed;
28251             }
28252             return this.encodeNamed;
28253         }
28254         // Numeric
28255         if (name.numeric) {
28256             return this.encodeNumeric;
28257         }
28258         // Raw encoder
28259         return this.encodeRaw;
28260     },
28261     /**
28262      * Decodes the specified string, this will replace entities with raw UTF characters.
28263      *
28264      * @method decode
28265      * @param {String} text Text to entity decode.
28266      * @return {String} Entity decoded string.
28267      */
28268     decode: function(text)
28269     {
28270         var  t = this;
28271         return text.replace(this.entityRegExp, function(all, numeric) {
28272             if (numeric) {
28273                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28274                 // Support upper UTF
28275                 if (numeric > 65535) {
28276                     numeric -= 65536;
28277                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28278                 }
28279                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28280             }
28281             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28282         });
28283     },
28284     nativeDecode : function (text) {
28285         return text;
28286     },
28287     makeMap : function (items, delim, map) {
28288                 var i;
28289                 items = items || [];
28290                 delim = delim || ',';
28291                 if (typeof items == "string") {
28292                         items = items.split(delim);
28293                 }
28294                 map = map || {};
28295                 i = items.length;
28296                 while (i--) {
28297                         map[items[i]] = {};
28298                 }
28299                 return map;
28300         }
28301 };
28302     
28303     
28304     
28305 Roo.htmleditor.TidyEntities.init();
28306 /**
28307  * @class Roo.htmleditor.KeyEnter
28308  * Handle Enter press..
28309  * @cfg {Roo.HtmlEditorCore} core the editor.
28310  * @constructor
28311  * Create a new Filter.
28312  * @param {Object} config Configuration options
28313  */
28314
28315
28316
28317
28318
28319 Roo.htmleditor.KeyEnter = function(cfg) {
28320     Roo.apply(this, cfg);
28321     // this does not actually call walk as it's really just a abstract class
28322  
28323     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28324 }
28325
28326 //Roo.htmleditor.KeyEnter.i = 0;
28327
28328
28329 Roo.htmleditor.KeyEnter.prototype = {
28330     
28331     core : false,
28332     
28333     keypress : function(e)
28334     {
28335         if (e.charCode != 13 && e.charCode != 10) {
28336             Roo.log([e.charCode,e]);
28337             return true;
28338         }
28339         e.preventDefault();
28340         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28341         var doc = this.core.doc;
28342           //add a new line
28343        
28344     
28345         var sel = this.core.getSelection();
28346         var range = sel.getRangeAt(0);
28347         var n = range.commonAncestorContainer;
28348         var pc = range.closest([ 'ol', 'ul']);
28349         var pli = range.closest('li');
28350         if (!pc || e.ctrlKey) {
28351             // on it list, or ctrl pressed.
28352             if (!e.ctrlKey) {
28353                 sel.insertNode('br', 'after'); 
28354             } else {
28355                 // only do this if we have ctrl key..
28356                 var br = doc.createElement('br');
28357                 br.className = 'clear';
28358                 br.setAttribute('style', 'clear: both');
28359                 sel.insertNode(br, 'after'); 
28360             }
28361             
28362          
28363             this.core.undoManager.addEvent();
28364             this.core.fireEditorEvent(e);
28365             return false;
28366         }
28367         
28368         // deal with <li> insetion
28369         if (pli.innerText.trim() == '' &&
28370             pli.previousSibling &&
28371             pli.previousSibling.nodeName == 'LI' &&
28372             pli.previousSibling.innerText.trim() ==  '') {
28373             pli.parentNode.removeChild(pli.previousSibling);
28374             sel.cursorAfter(pc);
28375             this.core.undoManager.addEvent();
28376             this.core.fireEditorEvent(e);
28377             return false;
28378         }
28379     
28380         var li = doc.createElement('LI');
28381         li.innerHTML = '&nbsp;';
28382         if (!pli || !pli.firstSibling) {
28383             pc.appendChild(li);
28384         } else {
28385             pli.parentNode.insertBefore(li, pli.firstSibling);
28386         }
28387         sel.cursorText (li.firstChild);
28388       
28389         this.core.undoManager.addEvent();
28390         this.core.fireEditorEvent(e);
28391
28392         return false;
28393         
28394     
28395         
28396         
28397          
28398     }
28399 };
28400      
28401 /**
28402  * @class Roo.htmleditor.Block
28403  * Base class for html editor blocks - do not use it directly .. extend it..
28404  * @cfg {DomElement} node The node to apply stuff to.
28405  * @cfg {String} friendly_name the name that appears in the context bar about this block
28406  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28407  
28408  * @constructor
28409  * Create a new Filter.
28410  * @param {Object} config Configuration options
28411  */
28412
28413 Roo.htmleditor.Block  = function(cfg)
28414 {
28415     // do nothing .. should not be called really.
28416 }
28417 /**
28418  * factory method to get the block from an element (using cache if necessary)
28419  * @static
28420  * @param {HtmlElement} the dom element
28421  */
28422 Roo.htmleditor.Block.factory = function(node)
28423 {
28424     var cc = Roo.htmleditor.Block.cache;
28425     var id = Roo.get(node).id;
28426     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28427         Roo.htmleditor.Block.cache[id].readElement(node);
28428         return Roo.htmleditor.Block.cache[id];
28429     }
28430     var db  = node.getAttribute('data-block');
28431     if (!db) {
28432         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28433     }
28434     var cls = Roo.htmleditor['Block' + db];
28435     if (typeof(cls) == 'undefined') {
28436         //Roo.log(node.getAttribute('data-block'));
28437         Roo.log("OOps missing block : " + 'Block' + db);
28438         return false;
28439     }
28440     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28441     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
28442 };
28443
28444 /**
28445  * initalize all Elements from content that are 'blockable'
28446  * @static
28447  * @param the body element
28448  */
28449 Roo.htmleditor.Block.initAll = function(body, type)
28450 {
28451     if (typeof(type) == 'undefined') {
28452         var ia = Roo.htmleditor.Block.initAll;
28453         ia(body,'table');
28454         ia(body,'td');
28455         ia(body,'figure');
28456         return;
28457     }
28458     Roo.each(Roo.get(body).query(type), function(e) {
28459         Roo.htmleditor.Block.factory(e);    
28460     },this);
28461 };
28462 // question goes here... do we need to clear out this cache sometimes?
28463 // or show we make it relivant to the htmleditor.
28464 Roo.htmleditor.Block.cache = {};
28465
28466 Roo.htmleditor.Block.prototype = {
28467     
28468     node : false,
28469     
28470      // used by context menu
28471     friendly_name : 'Based Block',
28472     
28473     // text for button to delete this element
28474     deleteTitle : false,
28475     
28476     context : false,
28477     /**
28478      * Update a node with values from this object
28479      * @param {DomElement} node
28480      */
28481     updateElement : function(node)
28482     {
28483         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28484     },
28485      /**
28486      * convert to plain HTML for calling insertAtCursor..
28487      */
28488     toHTML : function()
28489     {
28490         return Roo.DomHelper.markup(this.toObject());
28491     },
28492     /**
28493      * used by readEleemnt to extract data from a node
28494      * may need improving as it's pretty basic
28495      
28496      * @param {DomElement} node
28497      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28498      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28499      * @param {String} style the style property - eg. text-align
28500      */
28501     getVal : function(node, tag, attr, style)
28502     {
28503         var n = node;
28504         if (tag !== true && n.tagName != tag.toUpperCase()) {
28505             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28506             // but kiss for now.
28507             n = node.getElementsByTagName(tag).item(0);
28508         }
28509         if (!n) {
28510             return '';
28511         }
28512         if (attr === false) {
28513             return n;
28514         }
28515         if (attr == 'html') {
28516             return n.innerHTML;
28517         }
28518         if (attr == 'style') {
28519             return n.style[style]; 
28520         }
28521         
28522         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
28523             
28524     },
28525     /**
28526      * create a DomHelper friendly object - for use with 
28527      * Roo.DomHelper.markup / overwrite / etc..
28528      * (override this)
28529      */
28530     toObject : function()
28531     {
28532         return {};
28533     },
28534       /**
28535      * Read a node that has a 'data-block' property - and extract the values from it.
28536      * @param {DomElement} node - the node
28537      */
28538     readElement : function(node)
28539     {
28540         
28541     } 
28542     
28543     
28544 };
28545
28546  
28547
28548 /**
28549  * @class Roo.htmleditor.BlockFigure
28550  * Block that has an image and a figcaption
28551  * @cfg {String} image_src the url for the image
28552  * @cfg {String} align (left|right) alignment for the block default left
28553  * @cfg {String} caption the text to appear below  (and in the alt tag)
28554  * @cfg {String} caption_display (block|none) display or not the caption
28555  * @cfg {String|number} image_width the width of the image number or %?
28556  * @cfg {String|number} image_height the height of the image number or %?
28557  * 
28558  * @constructor
28559  * Create a new Filter.
28560  * @param {Object} config Configuration options
28561  */
28562
28563 Roo.htmleditor.BlockFigure = function(cfg)
28564 {
28565     if (cfg.node) {
28566         this.readElement(cfg.node);
28567         this.updateElement(cfg.node);
28568     }
28569     Roo.apply(this, cfg);
28570 }
28571 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
28572  
28573     
28574     // setable values.
28575     image_src: '',
28576     align: 'center',
28577     caption : '',
28578     caption_display : 'block',
28579     width : '100%',
28580     cls : '',
28581     href: '',
28582     video_url : '',
28583     
28584     // margin: '2%', not used
28585     
28586     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
28587
28588     
28589     // used by context menu
28590     friendly_name : 'Image with caption',
28591     deleteTitle : "Delete Image and Caption",
28592     
28593     contextMenu : function(toolbar)
28594     {
28595         
28596         var block = function() {
28597             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28598         };
28599         
28600         
28601         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28602         
28603         var syncValue = toolbar.editorcore.syncValue;
28604         
28605         var fields = {};
28606         
28607         return [
28608              {
28609                 xtype : 'TextItem',
28610                 text : "Source: ",
28611                 xns : rooui.Toolbar  //Boostrap?
28612             },
28613             {
28614                 xtype : 'Button',
28615                 text: 'Change Image URL',
28616                  
28617                 listeners : {
28618                     click: function (btn, state)
28619                     {
28620                         var b = block();
28621                         
28622                         Roo.MessageBox.show({
28623                             title : "Image Source URL",
28624                             msg : "Enter the url for the image",
28625                             buttons: Roo.MessageBox.OKCANCEL,
28626                             fn: function(btn, val){
28627                                 if (btn != 'ok') {
28628                                     return;
28629                                 }
28630                                 b.image_src = val;
28631                                 b.updateElement();
28632                                 syncValue();
28633                                 toolbar.editorcore.onEditorEvent();
28634                             },
28635                             minWidth:250,
28636                             prompt:true,
28637                             //multiline: multiline,
28638                             modal : true,
28639                             value : b.image_src
28640                         });
28641                     }
28642                 },
28643                 xns : rooui.Toolbar
28644             },
28645          
28646             {
28647                 xtype : 'Button',
28648                 text: 'Change Link URL',
28649                  
28650                 listeners : {
28651                     click: function (btn, state)
28652                     {
28653                         var b = block();
28654                         
28655                         Roo.MessageBox.show({
28656                             title : "Link URL",
28657                             msg : "Enter the url for the link - leave blank to have no link",
28658                             buttons: Roo.MessageBox.OKCANCEL,
28659                             fn: function(btn, val){
28660                                 if (btn != 'ok') {
28661                                     return;
28662                                 }
28663                                 b.href = val;
28664                                 b.updateElement();
28665                                 syncValue();
28666                                 toolbar.editorcore.onEditorEvent();
28667                             },
28668                             minWidth:250,
28669                             prompt:true,
28670                             //multiline: multiline,
28671                             modal : true,
28672                             value : b.href
28673                         });
28674                     }
28675                 },
28676                 xns : rooui.Toolbar
28677             },
28678             {
28679                 xtype : 'Button',
28680                 text: 'Show Video URL',
28681                  
28682                 listeners : {
28683                     click: function (btn, state)
28684                     {
28685                         Roo.MessageBox.alert("Video URL",
28686                             block().video_url == '' ? 'This image is not linked ot a video' :
28687                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
28688                     }
28689                 },
28690                 xns : rooui.Toolbar
28691             },
28692             
28693             
28694             {
28695                 xtype : 'TextItem',
28696                 text : "Width: ",
28697                 xns : rooui.Toolbar  //Boostrap?
28698             },
28699             {
28700                 xtype : 'ComboBox',
28701                 allowBlank : false,
28702                 displayField : 'val',
28703                 editable : true,
28704                 listWidth : 100,
28705                 triggerAction : 'all',
28706                 typeAhead : true,
28707                 valueField : 'val',
28708                 width : 70,
28709                 name : 'width',
28710                 listeners : {
28711                     select : function (combo, r, index)
28712                     {
28713                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28714                         var b = block();
28715                         b.width = r.get('val');
28716                         b.updateElement();
28717                         syncValue();
28718                         toolbar.editorcore.onEditorEvent();
28719                     }
28720                 },
28721                 xns : rooui.form,
28722                 store : {
28723                     xtype : 'SimpleStore',
28724                     data : [
28725                         ['100%'],
28726                         ['80%'],
28727                         ['50%'],
28728                         ['20%'],
28729                         ['10%']
28730                     ],
28731                     fields : [ 'val'],
28732                     xns : Roo.data
28733                 }
28734             },
28735             {
28736                 xtype : 'TextItem',
28737                 text : "Align: ",
28738                 xns : rooui.Toolbar  //Boostrap?
28739             },
28740             {
28741                 xtype : 'ComboBox',
28742                 allowBlank : false,
28743                 displayField : 'val',
28744                 editable : true,
28745                 listWidth : 100,
28746                 triggerAction : 'all',
28747                 typeAhead : true,
28748                 valueField : 'val',
28749                 width : 70,
28750                 name : 'align',
28751                 listeners : {
28752                     select : function (combo, r, index)
28753                     {
28754                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28755                         var b = block();
28756                         b.align = r.get('val');
28757                         b.updateElement();
28758                         syncValue();
28759                         toolbar.editorcore.onEditorEvent();
28760                     }
28761                 },
28762                 xns : rooui.form,
28763                 store : {
28764                     xtype : 'SimpleStore',
28765                     data : [
28766                         ['left'],
28767                         ['right'],
28768                         ['center']
28769                     ],
28770                     fields : [ 'val'],
28771                     xns : Roo.data
28772                 }
28773             },
28774             
28775             
28776             {
28777                 xtype : 'Button',
28778                 text: 'Hide Caption',
28779                 name : 'caption_display',
28780                 pressed : false,
28781                 enableToggle : true,
28782                 setValue : function(v) {
28783                     // this trigger toggle.
28784                      
28785                     this.setText(v ? "Hide Caption" : "Show Caption");
28786                     this.setPressed(v != 'block');
28787                 },
28788                 listeners : {
28789                     toggle: function (btn, state)
28790                     {
28791                         var b  = block();
28792                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
28793                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
28794                         b.updateElement();
28795                         syncValue();
28796                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28797                         toolbar.editorcore.onEditorEvent();
28798                     }
28799                 },
28800                 xns : rooui.Toolbar
28801             }
28802         ];
28803         
28804     },
28805     /**
28806      * create a DomHelper friendly object - for use with
28807      * Roo.DomHelper.markup / overwrite / etc..
28808      */
28809     toObject : function()
28810     {
28811         var d = document.createElement('div');
28812         d.innerHTML = this.caption;
28813         
28814         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
28815         
28816         var iw = this.align == 'center' ? this.width : '100%';
28817         var img =   {
28818             tag : 'img',
28819             contenteditable : 'false',
28820             src : this.image_src,
28821             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
28822             style: {
28823                 width : iw,
28824                 maxWidth : iw + ' !important', // this is not getting rendered?
28825                 margin : m  
28826                 
28827             }
28828         };
28829         /*
28830         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
28831                     '<a href="{2}">' + 
28832                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
28833                     '</a>' + 
28834                 '</div>',
28835         */
28836                 
28837         if (this.href.length > 0) {
28838             img = {
28839                 tag : 'a',
28840                 href: this.href,
28841                 contenteditable : 'true',
28842                 cn : [
28843                     img
28844                 ]
28845             };
28846         }
28847         
28848         
28849         if (this.video_url.length > 0) {
28850             img = {
28851                 tag : 'div',
28852                 cls : this.cls,
28853                 frameborder : 0,
28854                 allowfullscreen : true,
28855                 width : 420,  // these are for video tricks - that we replace the outer
28856                 height : 315,
28857                 src : this.video_url,
28858                 cn : [
28859                     img
28860                 ]
28861             };
28862         }
28863         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
28864         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
28865         
28866   
28867         var ret =   {
28868             tag: 'figure',
28869             'data-block' : 'Figure',
28870             'data-width' : this.width, 
28871             contenteditable : 'false',
28872             
28873             style : {
28874                 display: 'block',
28875                 float :  this.align ,
28876                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
28877                 width : this.align == 'center' ? '100%' : this.width,
28878                 margin:  '0px',
28879                 padding: this.align == 'center' ? '0' : '0 10px' ,
28880                 textAlign : this.align   // seems to work for email..
28881                 
28882             },
28883            
28884             
28885             align : this.align,
28886             cn : [
28887                 img,
28888               
28889                 {
28890                     tag: 'figcaption',
28891                     'data-display' : this.caption_display,
28892                     style : {
28893                         textAlign : 'left',
28894                         fontSize : '16px',
28895                         lineHeight : '24px',
28896                         display : this.caption_display,
28897                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
28898                         margin: m,
28899                         width: this.align == 'center' ?  this.width : '100%' 
28900                     
28901                          
28902                     },
28903                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
28904                     cn : [
28905                         {
28906                             tag: 'div',
28907                             style  : {
28908                                 marginTop : '16px',
28909                                 textAlign : 'left'
28910                             },
28911                             align: 'left',
28912                             cn : [
28913                                 {
28914                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
28915                                     tag : 'i',
28916                                     contenteditable : true,
28917                                     html : captionhtml
28918                                 }
28919                                 
28920                             ]
28921                         }
28922                         
28923                     ]
28924                     
28925                 }
28926             ]
28927         };
28928         return ret;
28929          
28930     },
28931     
28932     readElement : function(node)
28933     {
28934         // this should not really come from the link...
28935         this.video_url = this.getVal(node, 'div', 'src');
28936         this.cls = this.getVal(node, 'div', 'class');
28937         this.href = this.getVal(node, 'a', 'href');
28938         
28939         
28940         this.image_src = this.getVal(node, 'img', 'src');
28941          
28942         this.align = this.getVal(node, 'figure', 'align');
28943         var figcaption = this.getVal(node, 'figcaption', false);
28944         if (figcaption !== '') {
28945             this.caption = this.getVal(figcaption, 'i', 'html');
28946         }
28947         
28948
28949         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
28950         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
28951         this.width = this.getVal(node, true, 'data-width');
28952         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
28953         
28954     },
28955     removeNode : function()
28956     {
28957         return this.node;
28958     }
28959     
28960   
28961    
28962      
28963     
28964     
28965     
28966     
28967 })
28968
28969  
28970
28971 /**
28972  * @class Roo.htmleditor.BlockTable
28973  * Block that manages a table
28974  * 
28975  * @constructor
28976  * Create a new Filter.
28977  * @param {Object} config Configuration options
28978  */
28979
28980 Roo.htmleditor.BlockTable = function(cfg)
28981 {
28982     if (cfg.node) {
28983         this.readElement(cfg.node);
28984         this.updateElement(cfg.node);
28985     }
28986     Roo.apply(this, cfg);
28987     if (!cfg.node) {
28988         this.rows = [];
28989         for(var r = 0; r < this.no_row; r++) {
28990             this.rows[r] = [];
28991             for(var c = 0; c < this.no_col; c++) {
28992                 this.rows[r][c] = this.emptyCell();
28993             }
28994         }
28995     }
28996     
28997     
28998 }
28999 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29000  
29001     rows : false,
29002     no_col : 1,
29003     no_row : 1,
29004     
29005     
29006     width: '100%',
29007     
29008     // used by context menu
29009     friendly_name : 'Table',
29010     deleteTitle : 'Delete Table',
29011     // context menu is drawn once..
29012     
29013     contextMenu : function(toolbar)
29014     {
29015         
29016         var block = function() {
29017             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29018         };
29019         
29020         
29021         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29022         
29023         var syncValue = toolbar.editorcore.syncValue;
29024         
29025         var fields = {};
29026         
29027         return [
29028             {
29029                 xtype : 'TextItem',
29030                 text : "Width: ",
29031                 xns : rooui.Toolbar  //Boostrap?
29032             },
29033             {
29034                 xtype : 'ComboBox',
29035                 allowBlank : false,
29036                 displayField : 'val',
29037                 editable : true,
29038                 listWidth : 100,
29039                 triggerAction : 'all',
29040                 typeAhead : true,
29041                 valueField : 'val',
29042                 width : 100,
29043                 name : 'width',
29044                 listeners : {
29045                     select : function (combo, r, index)
29046                     {
29047                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29048                         var b = block();
29049                         b.width = r.get('val');
29050                         b.updateElement();
29051                         syncValue();
29052                         toolbar.editorcore.onEditorEvent();
29053                     }
29054                 },
29055                 xns : rooui.form,
29056                 store : {
29057                     xtype : 'SimpleStore',
29058                     data : [
29059                         ['100%'],
29060                         ['auto']
29061                     ],
29062                     fields : [ 'val'],
29063                     xns : Roo.data
29064                 }
29065             },
29066             // -------- Cols
29067             
29068             {
29069                 xtype : 'TextItem',
29070                 text : "Columns: ",
29071                 xns : rooui.Toolbar  //Boostrap?
29072             },
29073          
29074             {
29075                 xtype : 'Button',
29076                 text: '-',
29077                 listeners : {
29078                     click : function (_self, e)
29079                     {
29080                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29081                         block().removeColumn();
29082                         syncValue();
29083                         toolbar.editorcore.onEditorEvent();
29084                     }
29085                 },
29086                 xns : rooui.Toolbar
29087             },
29088             {
29089                 xtype : 'Button',
29090                 text: '+',
29091                 listeners : {
29092                     click : function (_self, e)
29093                     {
29094                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29095                         block().addColumn();
29096                         syncValue();
29097                         toolbar.editorcore.onEditorEvent();
29098                     }
29099                 },
29100                 xns : rooui.Toolbar
29101             },
29102             // -------- ROWS
29103             {
29104                 xtype : 'TextItem',
29105                 text : "Rows: ",
29106                 xns : rooui.Toolbar  //Boostrap?
29107             },
29108          
29109             {
29110                 xtype : 'Button',
29111                 text: '-',
29112                 listeners : {
29113                     click : function (_self, e)
29114                     {
29115                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29116                         block().removeRow();
29117                         syncValue();
29118                         toolbar.editorcore.onEditorEvent();
29119                     }
29120                 },
29121                 xns : rooui.Toolbar
29122             },
29123             {
29124                 xtype : 'Button',
29125                 text: '+',
29126                 listeners : {
29127                     click : function (_self, e)
29128                     {
29129                         block().addRow();
29130                         syncValue();
29131                         toolbar.editorcore.onEditorEvent();
29132                     }
29133                 },
29134                 xns : rooui.Toolbar
29135             },
29136             // -------- ROWS
29137             {
29138                 xtype : 'Button',
29139                 text: 'Reset Column Widths',
29140                 listeners : {
29141                     
29142                     click : function (_self, e)
29143                     {
29144                         block().resetWidths();
29145                         syncValue();
29146                         toolbar.editorcore.onEditorEvent();
29147                     }
29148                 },
29149                 xns : rooui.Toolbar
29150             } 
29151             
29152             
29153             
29154         ];
29155         
29156     },
29157     
29158     
29159   /**
29160      * create a DomHelper friendly object - for use with
29161      * Roo.DomHelper.markup / overwrite / etc..
29162      * ?? should it be called with option to hide all editing features?
29163      */
29164     toObject : function()
29165     {
29166         
29167         var ret = {
29168             tag : 'table',
29169             contenteditable : 'false', // this stops cell selection from picking the table.
29170             'data-block' : 'Table',
29171             style : {
29172                 width:  this.width,
29173                 border : 'solid 1px #000', // ??? hard coded?
29174                 'border-collapse' : 'collapse' 
29175             },
29176             cn : [
29177                 { tag : 'tbody' , cn : [] }
29178             ]
29179         };
29180         
29181         // do we have a head = not really 
29182         var ncols = 0;
29183         Roo.each(this.rows, function( row ) {
29184             var tr = {
29185                 tag: 'tr',
29186                 style : {
29187                     margin: '6px',
29188                     border : 'solid 1px #000',
29189                     textAlign : 'left' 
29190                 },
29191                 cn : [ ]
29192             };
29193             
29194             ret.cn[0].cn.push(tr);
29195             // does the row have any properties? ?? height?
29196             var nc = 0;
29197             Roo.each(row, function( cell ) {
29198                 
29199                 var td = {
29200                     tag : 'td',
29201                     contenteditable :  'true',
29202                     'data-block' : 'Td',
29203                     html : cell.html,
29204                     style : cell.style
29205                 };
29206                 if (cell.colspan > 1) {
29207                     td.colspan = cell.colspan ;
29208                     nc += cell.colspan;
29209                 } else {
29210                     nc++;
29211                 }
29212                 if (cell.rowspan > 1) {
29213                     td.rowspan = cell.rowspan ;
29214                 }
29215                 
29216                 
29217                 // widths ?
29218                 tr.cn.push(td);
29219                     
29220                 
29221             }, this);
29222             ncols = Math.max(nc, ncols);
29223             
29224             
29225         }, this);
29226         // add the header row..
29227         
29228         ncols++;
29229          
29230         
29231         return ret;
29232          
29233     },
29234     
29235     readElement : function(node)
29236     {
29237         node  = node ? node : this.node ;
29238         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29239         
29240         this.rows = [];
29241         this.no_row = 0;
29242         var trs = Array.from(node.rows);
29243         trs.forEach(function(tr) {
29244             var row =  [];
29245             this.rows.push(row);
29246             
29247             this.no_row++;
29248             var no_column = 0;
29249             Array.from(tr.cells).forEach(function(td) {
29250                 
29251                 var add = {
29252                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29253                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29254                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29255                     html : td.innerHTML
29256                 };
29257                 no_column += add.colspan;
29258                      
29259                 
29260                 row.push(add);
29261                 
29262                 
29263             },this);
29264             this.no_col = Math.max(this.no_col, no_column);
29265             
29266             
29267         },this);
29268         
29269         
29270     },
29271     normalizeRows: function()
29272     {
29273         var ret= [];
29274         var rid = -1;
29275         this.rows.forEach(function(row) {
29276             rid++;
29277             ret[rid] = [];
29278             row = this.normalizeRow(row);
29279             var cid = 0;
29280             row.forEach(function(c) {
29281                 while (typeof(ret[rid][cid]) != 'undefined') {
29282                     cid++;
29283                 }
29284                 if (typeof(ret[rid]) == 'undefined') {
29285                     ret[rid] = [];
29286                 }
29287                 ret[rid][cid] = c;
29288                 c.row = rid;
29289                 c.col = cid;
29290                 if (c.rowspan < 2) {
29291                     return;
29292                 }
29293                 
29294                 for(var i = 1 ;i < c.rowspan; i++) {
29295                     if (typeof(ret[rid+i]) == 'undefined') {
29296                         ret[rid+i] = [];
29297                     }
29298                     ret[rid+i][cid] = c;
29299                 }
29300             });
29301         }, this);
29302         return ret;
29303     
29304     },
29305     
29306     normalizeRow: function(row)
29307     {
29308         var ret= [];
29309         row.forEach(function(c) {
29310             if (c.colspan < 2) {
29311                 ret.push(c);
29312                 return;
29313             }
29314             for(var i =0 ;i < c.colspan; i++) {
29315                 ret.push(c);
29316             }
29317         });
29318         return ret;
29319     
29320     },
29321     
29322     deleteColumn : function(sel)
29323     {
29324         if (!sel || sel.type != 'col') {
29325             return;
29326         }
29327         if (this.no_col < 2) {
29328             return;
29329         }
29330         
29331         this.rows.forEach(function(row) {
29332             var cols = this.normalizeRow(row);
29333             var col = cols[sel.col];
29334             if (col.colspan > 1) {
29335                 col.colspan --;
29336             } else {
29337                 row.remove(col);
29338             }
29339             
29340         }, this);
29341         this.no_col--;
29342         
29343     },
29344     removeColumn : function()
29345     {
29346         this.deleteColumn({
29347             type: 'col',
29348             col : this.no_col-1
29349         });
29350         this.updateElement();
29351     },
29352     
29353      
29354     addColumn : function()
29355     {
29356         
29357         this.rows.forEach(function(row) {
29358             row.push(this.emptyCell());
29359            
29360         }, this);
29361         this.updateElement();
29362     },
29363     
29364     deleteRow : function(sel)
29365     {
29366         if (!sel || sel.type != 'row') {
29367             return;
29368         }
29369         
29370         if (this.no_row < 2) {
29371             return;
29372         }
29373         
29374         var rows = this.normalizeRows();
29375         
29376         
29377         rows[sel.row].forEach(function(col) {
29378             if (col.rowspan > 1) {
29379                 col.rowspan--;
29380             } else {
29381                 col.remove = 1; // flage it as removed.
29382             }
29383             
29384         }, this);
29385         var newrows = [];
29386         this.rows.forEach(function(row) {
29387             newrow = [];
29388             row.forEach(function(c) {
29389                 if (typeof(c.remove) == 'undefined') {
29390                     newrow.push(c);
29391                 }
29392                 
29393             });
29394             if (newrow.length > 0) {
29395                 newrows.push(row);
29396             }
29397         });
29398         this.rows =  newrows;
29399         
29400         
29401         
29402         this.no_row--;
29403         this.updateElement();
29404         
29405     },
29406     removeRow : function()
29407     {
29408         this.deleteRow({
29409             type: 'row',
29410             row : this.no_row-1
29411         });
29412         
29413     },
29414     
29415      
29416     addRow : function()
29417     {
29418         
29419         var row = [];
29420         for (var i = 0; i < this.no_col; i++ ) {
29421             
29422             row.push(this.emptyCell());
29423            
29424         }
29425         this.rows.push(row);
29426         this.updateElement();
29427         
29428     },
29429      
29430     // the default cell object... at present...
29431     emptyCell : function() {
29432         return (new Roo.htmleditor.BlockTd({})).toObject();
29433         
29434      
29435     },
29436     
29437     removeNode : function()
29438     {
29439         return this.node;
29440     },
29441     
29442     
29443     
29444     resetWidths : function()
29445     {
29446         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29447             var nn = Roo.htmleditor.Block.factory(n);
29448             nn.width = '';
29449             nn.updateElement(n);
29450         });
29451     }
29452     
29453     
29454     
29455     
29456 })
29457
29458 /**
29459  *
29460  * editing a TD?
29461  *
29462  * since selections really work on the table cell, then editing really should work from there
29463  *
29464  * The original plan was to support merging etc... - but that may not be needed yet..
29465  *
29466  * So this simple version will support:
29467  *   add/remove cols
29468  *   adjust the width +/-
29469  *   reset the width...
29470  *   
29471  *
29472  */
29473
29474
29475  
29476
29477 /**
29478  * @class Roo.htmleditor.BlockTable
29479  * Block that manages a table
29480  * 
29481  * @constructor
29482  * Create a new Filter.
29483  * @param {Object} config Configuration options
29484  */
29485
29486 Roo.htmleditor.BlockTd = function(cfg)
29487 {
29488     if (cfg.node) {
29489         this.readElement(cfg.node);
29490         this.updateElement(cfg.node);
29491     }
29492     Roo.apply(this, cfg);
29493      
29494     
29495     
29496 }
29497 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29498  
29499     node : false,
29500     
29501     width: '',
29502     textAlign : 'left',
29503     valign : 'top',
29504     
29505     colspan : 1,
29506     rowspan : 1,
29507     
29508     
29509     // used by context menu
29510     friendly_name : 'Table Cell',
29511     deleteTitle : false, // use our customer delete
29512     
29513     // context menu is drawn once..
29514     
29515     contextMenu : function(toolbar)
29516     {
29517         
29518         var cell = function() {
29519             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29520         };
29521         
29522         var table = function() {
29523             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
29524         };
29525         
29526         var lr = false;
29527         var saveSel = function()
29528         {
29529             lr = toolbar.editorcore.getSelection().getRangeAt(0);
29530         }
29531         var restoreSel = function()
29532         {
29533             if (lr) {
29534                 (function() {
29535                     toolbar.editorcore.focus();
29536                     var cr = toolbar.editorcore.getSelection();
29537                     cr.removeAllRanges();
29538                     cr.addRange(lr);
29539                     toolbar.editorcore.onEditorEvent();
29540                 }).defer(10, this);
29541                 
29542                 
29543             }
29544         }
29545         
29546         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29547         
29548         var syncValue = toolbar.editorcore.syncValue;
29549         
29550         var fields = {};
29551         
29552         return [
29553             {
29554                 xtype : 'Button',
29555                 text : 'Edit Table',
29556                 listeners : {
29557                     click : function() {
29558                         var t = toolbar.tb.selectedNode.closest('table');
29559                         toolbar.editorcore.selectNode(t);
29560                         toolbar.editorcore.onEditorEvent();                        
29561                     }
29562                 }
29563                 
29564             },
29565               
29566            
29567              
29568             {
29569                 xtype : 'TextItem',
29570                 text : "Column Width: ",
29571                  xns : rooui.Toolbar 
29572                
29573             },
29574             {
29575                 xtype : 'Button',
29576                 text: '-',
29577                 listeners : {
29578                     click : function (_self, e)
29579                     {
29580                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29581                         cell().shrinkColumn();
29582                         syncValue();
29583                          toolbar.editorcore.onEditorEvent();
29584                     }
29585                 },
29586                 xns : rooui.Toolbar
29587             },
29588             {
29589                 xtype : 'Button',
29590                 text: '+',
29591                 listeners : {
29592                     click : function (_self, e)
29593                     {
29594                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29595                         cell().growColumn();
29596                         syncValue();
29597                         toolbar.editorcore.onEditorEvent();
29598                     }
29599                 },
29600                 xns : rooui.Toolbar
29601             },
29602             
29603             {
29604                 xtype : 'TextItem',
29605                 text : "Vertical Align: ",
29606                 xns : rooui.Toolbar  //Boostrap?
29607             },
29608             {
29609                 xtype : 'ComboBox',
29610                 allowBlank : false,
29611                 displayField : 'val',
29612                 editable : true,
29613                 listWidth : 100,
29614                 triggerAction : 'all',
29615                 typeAhead : true,
29616                 valueField : 'val',
29617                 width : 100,
29618                 name : 'valign',
29619                 listeners : {
29620                     select : function (combo, r, index)
29621                     {
29622                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29623                         var b = cell();
29624                         b.valign = r.get('val');
29625                         b.updateElement();
29626                         syncValue();
29627                         toolbar.editorcore.onEditorEvent();
29628                     }
29629                 },
29630                 xns : rooui.form,
29631                 store : {
29632                     xtype : 'SimpleStore',
29633                     data : [
29634                         ['top'],
29635                         ['middle'],
29636                         ['bottom'] // there are afew more... 
29637                     ],
29638                     fields : [ 'val'],
29639                     xns : Roo.data
29640                 }
29641             },
29642             
29643             {
29644                 xtype : 'TextItem',
29645                 text : "Merge Cells: ",
29646                  xns : rooui.Toolbar 
29647                
29648             },
29649             
29650             
29651             {
29652                 xtype : 'Button',
29653                 text: 'Right',
29654                 listeners : {
29655                     click : function (_self, e)
29656                     {
29657                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29658                         cell().mergeRight();
29659                         //block().growColumn();
29660                         syncValue();
29661                         toolbar.editorcore.onEditorEvent();
29662                     }
29663                 },
29664                 xns : rooui.Toolbar
29665             },
29666              
29667             {
29668                 xtype : 'Button',
29669                 text: 'Below',
29670                 listeners : {
29671                     click : function (_self, e)
29672                     {
29673                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29674                         cell().mergeBelow();
29675                         //block().growColumn();
29676                         syncValue();
29677                         toolbar.editorcore.onEditorEvent();
29678                     }
29679                 },
29680                 xns : rooui.Toolbar
29681             },
29682             {
29683                 xtype : 'TextItem',
29684                 text : "| ",
29685                  xns : rooui.Toolbar 
29686                
29687             },
29688             
29689             {
29690                 xtype : 'Button',
29691                 text: 'Split',
29692                 listeners : {
29693                     click : function (_self, e)
29694                     {
29695                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29696                         cell().split();
29697                         syncValue();
29698                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29699                         toolbar.editorcore.onEditorEvent();
29700                                              
29701                     }
29702                 },
29703                 xns : rooui.Toolbar
29704             },
29705             {
29706                 xtype : 'Fill',
29707                 xns : rooui.Toolbar 
29708                
29709             },
29710         
29711           
29712             {
29713                 xtype : 'Button',
29714                 text: 'Delete',
29715                  
29716                 xns : rooui.Toolbar,
29717                 menu : {
29718                     xtype : 'Menu',
29719                     xns : rooui.menu,
29720                     items : [
29721                         {
29722                             xtype : 'Item',
29723                             html: 'Column',
29724                             listeners : {
29725                                 click : function (_self, e)
29726                                 {
29727                                     var t = table();
29728                                     
29729                                     cell().deleteColumn();
29730                                     syncValue();
29731                                     toolbar.editorcore.selectNode(t.node);
29732                                     toolbar.editorcore.onEditorEvent();   
29733                                 }
29734                             },
29735                             xns : rooui.menu
29736                         },
29737                         {
29738                             xtype : 'Item',
29739                             html: 'Row',
29740                             listeners : {
29741                                 click : function (_self, e)
29742                                 {
29743                                     var t = table();
29744                                     cell().deleteRow();
29745                                     syncValue();
29746                                     
29747                                     toolbar.editorcore.selectNode(t.node);
29748                                     toolbar.editorcore.onEditorEvent();   
29749                                                          
29750                                 }
29751                             },
29752                             xns : rooui.menu
29753                         },
29754                        {
29755                             xtype : 'Separator',
29756                             xns : rooui.menu
29757                         },
29758                         {
29759                             xtype : 'Item',
29760                             html: 'Table',
29761                             listeners : {
29762                                 click : function (_self, e)
29763                                 {
29764                                     var t = table();
29765                                     var nn = t.node.nextSibling || t.node.previousSibling;
29766                                     t.node.parentNode.removeChild(t.node);
29767                                     if (nn) { 
29768                                         toolbar.editorcore.selectNode(nn, true);
29769                                     }
29770                                     toolbar.editorcore.onEditorEvent();   
29771                                                          
29772                                 }
29773                             },
29774                             xns : rooui.menu
29775                         }
29776                     ]
29777                 }
29778             }
29779             
29780             // align... << fixme
29781             
29782         ];
29783         
29784     },
29785     
29786     
29787   /**
29788      * create a DomHelper friendly object - for use with
29789      * Roo.DomHelper.markup / overwrite / etc..
29790      * ?? should it be called with option to hide all editing features?
29791      */
29792  /**
29793      * create a DomHelper friendly object - for use with
29794      * Roo.DomHelper.markup / overwrite / etc..
29795      * ?? should it be called with option to hide all editing features?
29796      */
29797     toObject : function()
29798     {
29799         var ret = {
29800             tag : 'td',
29801             contenteditable : 'true', // this stops cell selection from picking the table.
29802             'data-block' : 'Td',
29803             valign : this.valign,
29804             style : {  
29805                 'text-align' :  this.textAlign,
29806                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
29807                 'border-collapse' : 'collapse',
29808                 padding : '6px', // 8 for desktop / 4 for mobile
29809                 'vertical-align': this.valign
29810             },
29811             html : this.html
29812         };
29813         if (this.width != '') {
29814             ret.width = this.width;
29815             ret.style.width = this.width;
29816         }
29817         
29818         
29819         if (this.colspan > 1) {
29820             ret.colspan = this.colspan ;
29821         } 
29822         if (this.rowspan > 1) {
29823             ret.rowspan = this.rowspan ;
29824         }
29825         
29826            
29827         
29828         return ret;
29829          
29830     },
29831     
29832     readElement : function(node)
29833     {
29834         node  = node ? node : this.node ;
29835         this.width = node.style.width;
29836         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
29837         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
29838         this.html = node.innerHTML;
29839         if (node.style.textAlign != '') {
29840             this.textAlign = node.style.textAlign;
29841         }
29842         
29843         
29844     },
29845      
29846     // the default cell object... at present...
29847     emptyCell : function() {
29848         return {
29849             colspan :  1,
29850             rowspan :  1,
29851             textAlign : 'left',
29852             html : "&nbsp;" // is this going to be editable now?
29853         };
29854      
29855     },
29856     
29857     removeNode : function()
29858     {
29859         return this.node.closest('table');
29860          
29861     },
29862     
29863     cellData : false,
29864     
29865     colWidths : false,
29866     
29867     toTableArray  : function()
29868     {
29869         var ret = [];
29870         var tab = this.node.closest('tr').closest('table');
29871         Array.from(tab.rows).forEach(function(r, ri){
29872             ret[ri] = [];
29873         });
29874         var rn = 0;
29875         this.colWidths = [];
29876         var all_auto = true;
29877         Array.from(tab.rows).forEach(function(r, ri){
29878             
29879             var cn = 0;
29880             Array.from(r.cells).forEach(function(ce, ci){
29881                 var c =  {
29882                     cell : ce,
29883                     row : rn,
29884                     col: cn,
29885                     colspan : ce.colSpan,
29886                     rowspan : ce.rowSpan
29887                 };
29888                 if (ce.isEqualNode(this.node)) {
29889                     this.cellData = c;
29890                 }
29891                 // if we have been filled up by a row?
29892                 if (typeof(ret[rn][cn]) != 'undefined') {
29893                     while(typeof(ret[rn][cn]) != 'undefined') {
29894                         cn++;
29895                     }
29896                     c.col = cn;
29897                 }
29898                 
29899                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
29900                     this.colWidths[cn] =   ce.style.width;
29901                     if (this.colWidths[cn] != '') {
29902                         all_auto = false;
29903                     }
29904                 }
29905                 
29906                 
29907                 if (c.colspan < 2 && c.rowspan < 2 ) {
29908                     ret[rn][cn] = c;
29909                     cn++;
29910                     return;
29911                 }
29912                 for(var j = 0; j < c.rowspan; j++) {
29913                     if (typeof(ret[rn+j]) == 'undefined') {
29914                         continue; // we have a problem..
29915                     }
29916                     ret[rn+j][cn] = c;
29917                     for(var i = 0; i < c.colspan; i++) {
29918                         ret[rn+j][cn+i] = c;
29919                     }
29920                 }
29921                 
29922                 cn += c.colspan;
29923             }, this);
29924             rn++;
29925         }, this);
29926         
29927         // initalize widths.?
29928         // either all widths or no widths..
29929         if (all_auto) {
29930             this.colWidths[0] = false; // no widths flag.
29931         }
29932         
29933         
29934         return ret;
29935         
29936     },
29937     
29938     
29939     
29940     
29941     mergeRight: function()
29942     {
29943          
29944         // get the contents of the next cell along..
29945         var tr = this.node.closest('tr');
29946         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
29947         if (i >= tr.childNodes.length - 1) {
29948             return; // no cells on right to merge with.
29949         }
29950         var table = this.toTableArray();
29951         
29952         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
29953             return; // nothing right?
29954         }
29955         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
29956         // right cell - must be same rowspan and on the same row.
29957         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
29958             return; // right hand side is not same rowspan.
29959         }
29960         
29961         
29962         
29963         this.node.innerHTML += ' ' + rc.cell.innerHTML;
29964         tr.removeChild(rc.cell);
29965         this.colspan += rc.colspan;
29966         this.node.setAttribute('colspan', this.colspan);
29967
29968         var table = this.toTableArray();
29969         this.normalizeWidths(table);
29970         this.updateWidths(table);
29971     },
29972     
29973     
29974     mergeBelow : function()
29975     {
29976         var table = this.toTableArray();
29977         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
29978             return; // no row below
29979         }
29980         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
29981             return; // nothing right?
29982         }
29983         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
29984         
29985         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
29986             return; // right hand side is not same rowspan.
29987         }
29988         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
29989         rc.cell.parentNode.removeChild(rc.cell);
29990         this.rowspan += rc.rowspan;
29991         this.node.setAttribute('rowspan', this.rowspan);
29992     },
29993     
29994     split: function()
29995     {
29996         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
29997             return;
29998         }
29999         var table = this.toTableArray();
30000         var cd = this.cellData;
30001         this.rowspan = 1;
30002         this.colspan = 1;
30003         
30004         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30005              
30006             
30007             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30008                 if (r == cd.row && c == cd.col) {
30009                     this.node.removeAttribute('rowspan');
30010                     this.node.removeAttribute('colspan');
30011                 }
30012                  
30013                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30014                 ntd.removeAttribute('id'); 
30015                 ntd.style.width  = this.colWidths[c];
30016                 ntd.innerHTML = '';
30017                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30018             }
30019             
30020         }
30021         this.redrawAllCells(table);
30022         
30023     },
30024     
30025     
30026     
30027     redrawAllCells: function(table)
30028     {
30029         
30030          
30031         var tab = this.node.closest('tr').closest('table');
30032         var ctr = tab.rows[0].parentNode;
30033         Array.from(tab.rows).forEach(function(r, ri){
30034             
30035             Array.from(r.cells).forEach(function(ce, ci){
30036                 ce.parentNode.removeChild(ce);
30037             });
30038             r.parentNode.removeChild(r);
30039         });
30040         for(var r = 0 ; r < table.length; r++) {
30041             var re = tab.rows[r];
30042             
30043             var re = tab.ownerDocument.createElement('tr');
30044             ctr.appendChild(re);
30045             for(var c = 0 ; c < table[r].length; c++) {
30046                 if (table[r][c].cell === false) {
30047                     continue;
30048                 }
30049                 
30050                 re.appendChild(table[r][c].cell);
30051                  
30052                 table[r][c].cell = false;
30053             }
30054         }
30055         
30056     },
30057     updateWidths : function(table)
30058     {
30059         for(var r = 0 ; r < table.length; r++) {
30060            
30061             for(var c = 0 ; c < table[r].length; c++) {
30062                 if (table[r][c].cell === false) {
30063                     continue;
30064                 }
30065                 
30066                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30067                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30068                     el.width = Math.floor(this.colWidths[c])  +'%';
30069                     el.updateElement(el.node);
30070                 }
30071                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30072                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30073                     var width = 0;
30074                     for(var i = 0; i < table[r][c].colspan; i ++) {
30075                         width += Math.floor(this.colWidths[c + i]);
30076                     }
30077                     el.width = width  +'%';
30078                     el.updateElement(el.node);
30079                 }
30080                 table[r][c].cell = false; // done
30081             }
30082         }
30083     },
30084     normalizeWidths : function(table)
30085     {
30086         if (this.colWidths[0] === false) {
30087             var nw = 100.0 / this.colWidths.length;
30088             this.colWidths.forEach(function(w,i) {
30089                 this.colWidths[i] = nw;
30090             },this);
30091             return;
30092         }
30093     
30094         var t = 0, missing = [];
30095         
30096         this.colWidths.forEach(function(w,i) {
30097             //if you mix % and
30098             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30099             var add =  this.colWidths[i];
30100             if (add > 0) {
30101                 t+=add;
30102                 return;
30103             }
30104             missing.push(i);
30105             
30106             
30107         },this);
30108         var nc = this.colWidths.length;
30109         if (missing.length) {
30110             var mult = (nc - missing.length) / (1.0 * nc);
30111             var t = mult * t;
30112             var ew = (100 -t) / (1.0 * missing.length);
30113             this.colWidths.forEach(function(w,i) {
30114                 if (w > 0) {
30115                     this.colWidths[i] = w * mult;
30116                     return;
30117                 }
30118                 
30119                 this.colWidths[i] = ew;
30120             }, this);
30121             // have to make up numbers..
30122              
30123         }
30124         // now we should have all the widths..
30125         
30126     
30127     },
30128     
30129     shrinkColumn : function()
30130     {
30131         var table = this.toTableArray();
30132         this.normalizeWidths(table);
30133         var col = this.cellData.col;
30134         var nw = this.colWidths[col] * 0.8;
30135         if (nw < 5) {
30136             return;
30137         }
30138         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30139         this.colWidths.forEach(function(w,i) {
30140             if (i == col) {
30141                  this.colWidths[i] = nw;
30142                 return;
30143             }
30144             this.colWidths[i] += otherAdd
30145         }, this);
30146         this.updateWidths(table);
30147          
30148     },
30149     growColumn : function()
30150     {
30151         var table = this.toTableArray();
30152         this.normalizeWidths(table);
30153         var col = this.cellData.col;
30154         var nw = this.colWidths[col] * 1.2;
30155         if (nw > 90) {
30156             return;
30157         }
30158         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30159         this.colWidths.forEach(function(w,i) {
30160             if (i == col) {
30161                 this.colWidths[i] = nw;
30162                 return;
30163             }
30164             this.colWidths[i] -= otherSub
30165         }, this);
30166         this.updateWidths(table);
30167          
30168     },
30169     deleteRow : function()
30170     {
30171         // delete this rows 'tr'
30172         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30173         // then reduce the rowspan.
30174         var table = this.toTableArray();
30175         // this.cellData.row;
30176         for (var i =0;i< table[this.cellData.row].length ; i++) {
30177             var c = table[this.cellData.row][i];
30178             if (c.row != this.cellData.row) {
30179                 
30180                 c.rowspan--;
30181                 c.cell.setAttribute('rowspan', c.rowspan);
30182                 continue;
30183             }
30184             if (c.rowspan > 1) {
30185                 c.rowspan--;
30186                 c.cell.setAttribute('rowspan', c.rowspan);
30187             }
30188         }
30189         table.splice(this.cellData.row,1);
30190         this.redrawAllCells(table);
30191         
30192     },
30193     deleteColumn : function()
30194     {
30195         var table = this.toTableArray();
30196         
30197         for (var i =0;i< table.length ; i++) {
30198             var c = table[i][this.cellData.col];
30199             if (c.col != this.cellData.col) {
30200                 table[i][this.cellData.col].colspan--;
30201             } else if (c.colspan > 1) {
30202                 c.colspan--;
30203                 c.cell.setAttribute('colspan', c.colspan);
30204             }
30205             table[i].splice(this.cellData.col,1);
30206         }
30207         
30208         this.redrawAllCells(table);
30209     }
30210     
30211     
30212     
30213     
30214 })
30215
30216 //<script type="text/javascript">
30217
30218 /*
30219  * Based  Ext JS Library 1.1.1
30220  * Copyright(c) 2006-2007, Ext JS, LLC.
30221  * LGPL
30222  *
30223  */
30224  
30225 /**
30226  * @class Roo.HtmlEditorCore
30227  * @extends Roo.Component
30228  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30229  *
30230  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30231  */
30232
30233 Roo.HtmlEditorCore = function(config){
30234     
30235     
30236     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30237     
30238     
30239     this.addEvents({
30240         /**
30241          * @event initialize
30242          * Fires when the editor is fully initialized (including the iframe)
30243          * @param {Roo.HtmlEditorCore} this
30244          */
30245         initialize: true,
30246         /**
30247          * @event activate
30248          * Fires when the editor is first receives the focus. Any insertion must wait
30249          * until after this event.
30250          * @param {Roo.HtmlEditorCore} this
30251          */
30252         activate: true,
30253          /**
30254          * @event beforesync
30255          * Fires before the textarea is updated with content from the editor iframe. Return false
30256          * to cancel the sync.
30257          * @param {Roo.HtmlEditorCore} this
30258          * @param {String} html
30259          */
30260         beforesync: true,
30261          /**
30262          * @event beforepush
30263          * Fires before the iframe editor is updated with content from the textarea. Return false
30264          * to cancel the push.
30265          * @param {Roo.HtmlEditorCore} this
30266          * @param {String} html
30267          */
30268         beforepush: true,
30269          /**
30270          * @event sync
30271          * Fires when the textarea is updated with content from the editor iframe.
30272          * @param {Roo.HtmlEditorCore} this
30273          * @param {String} html
30274          */
30275         sync: true,
30276          /**
30277          * @event push
30278          * Fires when the iframe editor is updated with content from the textarea.
30279          * @param {Roo.HtmlEditorCore} this
30280          * @param {String} html
30281          */
30282         push: true,
30283         
30284         /**
30285          * @event editorevent
30286          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30287          * @param {Roo.HtmlEditorCore} this
30288          */
30289         editorevent: true 
30290          
30291         
30292     });
30293     
30294     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30295     
30296     // defaults : white / black...
30297     this.applyBlacklists();
30298     
30299     
30300     
30301 };
30302
30303
30304 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30305
30306
30307      /**
30308      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30309      */
30310     
30311     owner : false,
30312     
30313      /**
30314      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30315      *                        Roo.resizable.
30316      */
30317     resizable : false,
30318      /**
30319      * @cfg {Number} height (in pixels)
30320      */   
30321     height: 300,
30322    /**
30323      * @cfg {Number} width (in pixels)
30324      */   
30325     width: 500,
30326      /**
30327      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30328      *         if you are doing an email editor, this probably needs disabling, it's designed
30329      */
30330     autoClean: true,
30331     
30332     /**
30333      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30334      */
30335     enableBlocks : true,
30336     /**
30337      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30338      * 
30339      */
30340     stylesheets: false,
30341      /**
30342      * @cfg {String} language default en - language of text (usefull for rtl languages)
30343      * 
30344      */
30345     language: 'en',
30346     
30347     /**
30348      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30349      *          - by default they are stripped - if you are editing email you may need this.
30350      */
30351     allowComments: false,
30352     // id of frame..
30353     frameId: false,
30354     
30355     // private properties
30356     validationEvent : false,
30357     deferHeight: true,
30358     initialized : false,
30359     activated : false,
30360     sourceEditMode : false,
30361     onFocus : Roo.emptyFn,
30362     iframePad:3,
30363     hideMode:'offsets',
30364     
30365     clearUp: true,
30366     
30367     // blacklist + whitelisted elements..
30368     black: false,
30369     white: false,
30370      
30371     bodyCls : '',
30372
30373     
30374     undoManager : false,
30375     /**
30376      * Protected method that will not generally be called directly. It
30377      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30378      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30379      */
30380     getDocMarkup : function(){
30381         // body styles..
30382         var st = '';
30383         
30384         // inherit styels from page...?? 
30385         if (this.stylesheets === false) {
30386             
30387             Roo.get(document.head).select('style').each(function(node) {
30388                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30389             });
30390             
30391             Roo.get(document.head).select('link').each(function(node) { 
30392                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30393             });
30394             
30395         } else if (!this.stylesheets.length) {
30396                 // simple..
30397                 st = '<style type="text/css">' +
30398                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30399                    '</style>';
30400         } else {
30401             for (var i in this.stylesheets) {
30402                 if (typeof(this.stylesheets[i]) != 'string') {
30403                     continue;
30404                 }
30405                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30406             }
30407             
30408         }
30409         
30410         st +=  '<style type="text/css">' +
30411             'IMG { cursor: pointer } ' +
30412         '</style>';
30413         
30414         st += '<meta name="google" content="notranslate">';
30415         
30416         var cls = 'notranslate roo-htmleditor-body';
30417         
30418         if(this.bodyCls.length){
30419             cls += ' ' + this.bodyCls;
30420         }
30421         
30422         return '<html  class="notranslate" translate="no"><head>' + st  +
30423             //<style type="text/css">' +
30424             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30425             //'</style>' +
30426             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30427     },
30428
30429     // private
30430     onRender : function(ct, position)
30431     {
30432         var _t = this;
30433         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30434         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30435         
30436         
30437         this.el.dom.style.border = '0 none';
30438         this.el.dom.setAttribute('tabIndex', -1);
30439         this.el.addClass('x-hidden hide');
30440         
30441         
30442         
30443         if(Roo.isIE){ // fix IE 1px bogus margin
30444             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30445         }
30446        
30447         
30448         this.frameId = Roo.id();
30449         
30450          
30451         
30452         var iframe = this.owner.wrap.createChild({
30453             tag: 'iframe',
30454             cls: 'form-control', // bootstrap..
30455             id: this.frameId,
30456             name: this.frameId,
30457             frameBorder : 'no',
30458             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
30459         }, this.el
30460         );
30461         
30462         
30463         this.iframe = iframe.dom;
30464
30465         this.assignDocWin();
30466         
30467         this.doc.designMode = 'on';
30468        
30469         this.doc.open();
30470         this.doc.write(this.getDocMarkup());
30471         this.doc.close();
30472
30473         
30474         var task = { // must defer to wait for browser to be ready
30475             run : function(){
30476                 //console.log("run task?" + this.doc.readyState);
30477                 this.assignDocWin();
30478                 if(this.doc.body || this.doc.readyState == 'complete'){
30479                     try {
30480                         this.doc.designMode="on";
30481                         
30482                     } catch (e) {
30483                         return;
30484                     }
30485                     Roo.TaskMgr.stop(task);
30486                     this.initEditor.defer(10, this);
30487                 }
30488             },
30489             interval : 10,
30490             duration: 10000,
30491             scope: this
30492         };
30493         Roo.TaskMgr.start(task);
30494
30495     },
30496
30497     // private
30498     onResize : function(w, h)
30499     {
30500          Roo.log('resize: ' +w + ',' + h );
30501         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30502         if(!this.iframe){
30503             return;
30504         }
30505         if(typeof w == 'number'){
30506             
30507             this.iframe.style.width = w + 'px';
30508         }
30509         if(typeof h == 'number'){
30510             
30511             this.iframe.style.height = h + 'px';
30512             if(this.doc){
30513                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
30514             }
30515         }
30516         
30517     },
30518
30519     /**
30520      * Toggles the editor between standard and source edit mode.
30521      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30522      */
30523     toggleSourceEdit : function(sourceEditMode){
30524         
30525         this.sourceEditMode = sourceEditMode === true;
30526         
30527         if(this.sourceEditMode){
30528  
30529             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
30530             
30531         }else{
30532             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
30533             //this.iframe.className = '';
30534             this.deferFocus();
30535         }
30536         //this.setSize(this.owner.wrap.getSize());
30537         //this.fireEvent('editmodechange', this, this.sourceEditMode);
30538     },
30539
30540     
30541   
30542
30543     /**
30544      * Protected method that will not generally be called directly. If you need/want
30545      * custom HTML cleanup, this is the method you should override.
30546      * @param {String} html The HTML to be cleaned
30547      * return {String} The cleaned HTML
30548      */
30549     cleanHtml : function(html)
30550     {
30551         html = String(html);
30552         if(html.length > 5){
30553             if(Roo.isSafari){ // strip safari nonsense
30554                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
30555             }
30556         }
30557         if(html == '&nbsp;'){
30558             html = '';
30559         }
30560         return html;
30561     },
30562
30563     /**
30564      * HTML Editor -> Textarea
30565      * Protected method that will not generally be called directly. Syncs the contents
30566      * of the editor iframe with the textarea.
30567      */
30568     syncValue : function()
30569     {
30570         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
30571         if(this.initialized){
30572             
30573             if (this.undoManager) {
30574                 this.undoManager.addEvent();
30575             }
30576
30577             
30578             var bd = (this.doc.body || this.doc.documentElement);
30579            
30580             
30581             var sel = this.win.getSelection();
30582             
30583             var div = document.createElement('div');
30584             div.innerHTML = bd.innerHTML;
30585             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
30586             if (gtx.length > 0) {
30587                 var rm = gtx.item(0).parentNode;
30588                 rm.parentNode.removeChild(rm);
30589             }
30590             
30591            
30592             if (this.enableBlocks) {
30593                 new Roo.htmleditor.FilterBlock({ node : div });
30594             }
30595             
30596             var html = div.innerHTML;
30597             
30598             //?? tidy?
30599             if (this.autoClean) {
30600                 
30601                 new Roo.htmleditor.FilterAttributes({
30602                     node : div,
30603                     attrib_white : [
30604                             'href',
30605                             'src',
30606                             'name',
30607                             'align',
30608                             'colspan',
30609                             'rowspan',
30610                             'data-display',
30611                             'data-width',
30612                             'start' ,
30613                             'style',
30614                             // youtube embed.
30615                             'class',
30616                             'allowfullscreen',
30617                             'frameborder',
30618                             'width',
30619                             'height',
30620                             'alt'
30621                             ],
30622                     attrib_clean : ['href', 'src' ] 
30623                 });
30624                 
30625                 var tidy = new Roo.htmleditor.TidySerializer({
30626                     inner:  true
30627                 });
30628                 html  = tidy.serialize(div);
30629                 
30630             }
30631             
30632             
30633             if(Roo.isSafari){
30634                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
30635                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
30636                 if(m && m[1]){
30637                     html = '<div style="'+m[0]+'">' + html + '</div>';
30638                 }
30639             }
30640             html = this.cleanHtml(html);
30641             // fix up the special chars.. normaly like back quotes in word...
30642             // however we do not want to do this with chinese..
30643             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
30644                 
30645                 var cc = match.charCodeAt();
30646
30647                 // Get the character value, handling surrogate pairs
30648                 if (match.length == 2) {
30649                     // It's a surrogate pair, calculate the Unicode code point
30650                     var high = match.charCodeAt(0) - 0xD800;
30651                     var low  = match.charCodeAt(1) - 0xDC00;
30652                     cc = (high * 0x400) + low + 0x10000;
30653                 }  else if (
30654                     (cc >= 0x4E00 && cc < 0xA000 ) ||
30655                     (cc >= 0x3400 && cc < 0x4E00 ) ||
30656                     (cc >= 0xf900 && cc < 0xfb00 )
30657                 ) {
30658                         return match;
30659                 }  
30660          
30661                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
30662                 return "&#" + cc + ";";
30663                 
30664                 
30665             });
30666             
30667             
30668              
30669             if(this.owner.fireEvent('beforesync', this, html) !== false){
30670                 this.el.dom.value = html;
30671                 this.owner.fireEvent('sync', this, html);
30672             }
30673         }
30674     },
30675
30676     /**
30677      * TEXTAREA -> EDITABLE
30678      * Protected method that will not generally be called directly. Pushes the value of the textarea
30679      * into the iframe editor.
30680      */
30681     pushValue : function()
30682     {
30683         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
30684         if(this.initialized){
30685             var v = this.el.dom.value.trim();
30686             
30687             
30688             if(this.owner.fireEvent('beforepush', this, v) !== false){
30689                 var d = (this.doc.body || this.doc.documentElement);
30690                 d.innerHTML = v;
30691                  
30692                 this.el.dom.value = d.innerHTML;
30693                 this.owner.fireEvent('push', this, v);
30694             }
30695             if (this.autoClean) {
30696                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
30697                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
30698             }
30699             if (this.enableBlocks) {
30700                 Roo.htmleditor.Block.initAll(this.doc.body);
30701             }
30702             
30703             this.updateLanguage();
30704             
30705             var lc = this.doc.body.lastChild;
30706             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
30707                 // add an extra line at the end.
30708                 this.doc.body.appendChild(this.doc.createElement('br'));
30709             }
30710             
30711             
30712         }
30713     },
30714
30715     // private
30716     deferFocus : function(){
30717         this.focus.defer(10, this);
30718     },
30719
30720     // doc'ed in Field
30721     focus : function(){
30722         if(this.win && !this.sourceEditMode){
30723             this.win.focus();
30724         }else{
30725             this.el.focus();
30726         }
30727     },
30728     
30729     assignDocWin: function()
30730     {
30731         var iframe = this.iframe;
30732         
30733          if(Roo.isIE){
30734             this.doc = iframe.contentWindow.document;
30735             this.win = iframe.contentWindow;
30736         } else {
30737 //            if (!Roo.get(this.frameId)) {
30738 //                return;
30739 //            }
30740 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30741 //            this.win = Roo.get(this.frameId).dom.contentWindow;
30742             
30743             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
30744                 return;
30745             }
30746             
30747             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30748             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
30749         }
30750     },
30751     
30752     // private
30753     initEditor : function(){
30754         //console.log("INIT EDITOR");
30755         this.assignDocWin();
30756         
30757         
30758         
30759         this.doc.designMode="on";
30760         this.doc.open();
30761         this.doc.write(this.getDocMarkup());
30762         this.doc.close();
30763         
30764         var dbody = (this.doc.body || this.doc.documentElement);
30765         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
30766         // this copies styles from the containing element into thsi one..
30767         // not sure why we need all of this..
30768         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
30769         
30770         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
30771         //ss['background-attachment'] = 'fixed'; // w3c
30772         dbody.bgProperties = 'fixed'; // ie
30773         dbody.setAttribute("translate", "no");
30774         
30775         //Roo.DomHelper.applyStyles(dbody, ss);
30776         Roo.EventManager.on(this.doc, {
30777              
30778             'mouseup': this.onEditorEvent,
30779             'dblclick': this.onEditorEvent,
30780             'click': this.onEditorEvent,
30781             'keyup': this.onEditorEvent,
30782             
30783             buffer:100,
30784             scope: this
30785         });
30786         Roo.EventManager.on(this.doc, {
30787             'paste': this.onPasteEvent,
30788             scope : this
30789         });
30790         if(Roo.isGecko){
30791             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
30792         }
30793         //??? needed???
30794         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
30795             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
30796         }
30797         this.initialized = true;
30798
30799         
30800         // initialize special key events - enter
30801         new Roo.htmleditor.KeyEnter({core : this});
30802         
30803          
30804         
30805         this.owner.fireEvent('initialize', this);
30806         this.pushValue();
30807     },
30808     // this is to prevent a href clicks resulting in a redirect?
30809    
30810     onPasteEvent : function(e,v)
30811     {
30812         // I think we better assume paste is going to be a dirty load of rubish from word..
30813         
30814         // even pasting into a 'email version' of this widget will have to clean up that mess.
30815         var cd = (e.browserEvent.clipboardData || window.clipboardData);
30816         
30817         // check what type of paste - if it's an image, then handle it differently.
30818         if (cd.files && cd.files.length > 0) {
30819             // pasting images?
30820             var urlAPI = (window.createObjectURL && window) || 
30821                 (window.URL && URL.revokeObjectURL && URL) || 
30822                 (window.webkitURL && webkitURL);
30823     
30824             var url = urlAPI.createObjectURL( cd.files[0]);
30825             this.insertAtCursor('<img src=" + url + ">');
30826             return false;
30827         }
30828         if (cd.types.indexOf('text/html') < 0 ) {
30829             return false;
30830         }
30831         var images = [];
30832         var html = cd.getData('text/html'); // clipboard event
30833         if (cd.types.indexOf('text/rtf') > -1) {
30834             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
30835             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
30836         }
30837         //Roo.log(images);
30838         //Roo.log(imgs);
30839         // fixme..
30840         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
30841                        .map(function(g) { return g.toDataURL(); })
30842                        .filter(function(g) { return g != 'about:blank'; });
30843         
30844         //Roo.log(html);
30845         html = this.cleanWordChars(html);
30846         
30847         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
30848         
30849         
30850         var sn = this.getParentElement();
30851         // check if d contains a table, and prevent nesting??
30852         //Roo.log(d.getElementsByTagName('table'));
30853         //Roo.log(sn);
30854         //Roo.log(sn.closest('table'));
30855         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
30856             e.preventDefault();
30857             this.insertAtCursor("You can not nest tables");
30858             //Roo.log("prevent?"); // fixme - 
30859             return false;
30860         }
30861         
30862         
30863         
30864         if (images.length > 0) {
30865             // replace all v:imagedata - with img.
30866             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
30867             Roo.each(ar, function(node) {
30868                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
30869                 node.parentNode.removeChild(node);
30870             });
30871             
30872             
30873             Roo.each(d.getElementsByTagName('img'), function(img, i) {
30874                 img.setAttribute('src', images[i]);
30875             });
30876         }
30877         if (this.autoClean) {
30878             new Roo.htmleditor.FilterWord({ node : d });
30879             
30880             new Roo.htmleditor.FilterStyleToTag({ node : d });
30881             new Roo.htmleditor.FilterAttributes({
30882                 node : d,
30883                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30884                 attrib_clean : ['href', 'src' ] 
30885             });
30886             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
30887             // should be fonts..
30888             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
30889             new Roo.htmleditor.FilterParagraph({ node : d });
30890             new Roo.htmleditor.FilterSpan({ node : d });
30891             new Roo.htmleditor.FilterLongBr({ node : d });
30892             new Roo.htmleditor.FilterComment({ node : d });
30893             
30894             
30895         }
30896         if (this.enableBlocks) {
30897                 
30898             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
30899                 if (img.closest('figure')) { // assume!! that it's aready
30900                     return;
30901                 }
30902                 var fig  = new Roo.htmleditor.BlockFigure({
30903                     image_src  : img.src
30904                 });
30905                 fig.updateElement(img); // replace it..
30906                 
30907             });
30908         }
30909         
30910         
30911         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
30912         if (this.enableBlocks) {
30913             Roo.htmleditor.Block.initAll(this.doc.body);
30914         }
30915          
30916         
30917         e.preventDefault();
30918         return false;
30919         // default behaveiour should be our local cleanup paste? (optional?)
30920         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
30921         //this.owner.fireEvent('paste', e, v);
30922     },
30923     // private
30924     onDestroy : function(){
30925         
30926         
30927         
30928         if(this.rendered){
30929             
30930             //for (var i =0; i < this.toolbars.length;i++) {
30931             //    // fixme - ask toolbars for heights?
30932             //    this.toolbars[i].onDestroy();
30933            // }
30934             
30935             //this.wrap.dom.innerHTML = '';
30936             //this.wrap.remove();
30937         }
30938     },
30939
30940     // private
30941     onFirstFocus : function(){
30942         
30943         this.assignDocWin();
30944         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
30945         
30946         this.activated = true;
30947          
30948     
30949         if(Roo.isGecko){ // prevent silly gecko errors
30950             this.win.focus();
30951             var s = this.win.getSelection();
30952             if(!s.focusNode || s.focusNode.nodeType != 3){
30953                 var r = s.getRangeAt(0);
30954                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
30955                 r.collapse(true);
30956                 this.deferFocus();
30957             }
30958             try{
30959                 this.execCmd('useCSS', true);
30960                 this.execCmd('styleWithCSS', false);
30961             }catch(e){}
30962         }
30963         this.owner.fireEvent('activate', this);
30964     },
30965
30966     // private
30967     adjustFont: function(btn){
30968         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
30969         //if(Roo.isSafari){ // safari
30970         //    adjust *= 2;
30971        // }
30972         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
30973         if(Roo.isSafari){ // safari
30974             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
30975             v =  (v < 10) ? 10 : v;
30976             v =  (v > 48) ? 48 : v;
30977             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
30978             
30979         }
30980         
30981         
30982         v = Math.max(1, v+adjust);
30983         
30984         this.execCmd('FontSize', v  );
30985     },
30986
30987     onEditorEvent : function(e)
30988     {
30989          
30990         
30991         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
30992             return; // we do not handle this.. (undo manager does..)
30993         }
30994         // in theory this detects if the last element is not a br, then we try and do that.
30995         // its so clicking in space at bottom triggers adding a br and moving the cursor.
30996         if (e &&
30997             e.target.nodeName == 'BODY' &&
30998             e.type == "mouseup" &&
30999             this.doc.body.lastChild
31000            ) {
31001             var lc = this.doc.body.lastChild;
31002             // gtx-trans is google translate plugin adding crap.
31003             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31004                 lc = lc.previousSibling;
31005             }
31006             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31007             // if last element is <BR> - then dont do anything.
31008             
31009                 var ns = this.doc.createElement('br');
31010                 this.doc.body.appendChild(ns);
31011                 range = this.doc.createRange();
31012                 range.setStartAfter(ns);
31013                 range.collapse(true);
31014                 var sel = this.win.getSelection();
31015                 sel.removeAllRanges();
31016                 sel.addRange(range);
31017             }
31018         }
31019         
31020         
31021         
31022         this.fireEditorEvent(e);
31023       //  this.updateToolbar();
31024         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31025     },
31026     
31027     fireEditorEvent: function(e)
31028     {
31029         this.owner.fireEvent('editorevent', this, e);
31030     },
31031
31032     insertTag : function(tg)
31033     {
31034         // could be a bit smarter... -> wrap the current selected tRoo..
31035         if (tg.toLowerCase() == 'span' ||
31036             tg.toLowerCase() == 'code' ||
31037             tg.toLowerCase() == 'sup' ||
31038             tg.toLowerCase() == 'sub' 
31039             ) {
31040             
31041             range = this.createRange(this.getSelection());
31042             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31043             wrappingNode.appendChild(range.extractContents());
31044             range.insertNode(wrappingNode);
31045
31046             return;
31047             
31048             
31049             
31050         }
31051         this.execCmd("formatblock",   tg);
31052         this.undoManager.addEvent(); 
31053     },
31054     
31055     insertText : function(txt)
31056     {
31057         
31058         
31059         var range = this.createRange();
31060         range.deleteContents();
31061                //alert(Sender.getAttribute('label'));
31062                
31063         range.insertNode(this.doc.createTextNode(txt));
31064         this.undoManager.addEvent();
31065     } ,
31066     
31067      
31068
31069     /**
31070      * Executes a Midas editor command on the editor document and performs necessary focus and
31071      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31072      * @param {String} cmd The Midas command
31073      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31074      */
31075     relayCmd : function(cmd, value)
31076     {
31077         
31078         switch (cmd) {
31079             case 'justifyleft':
31080             case 'justifyright':
31081             case 'justifycenter':
31082                 // if we are in a cell, then we will adjust the
31083                 var n = this.getParentElement();
31084                 var td = n.closest('td');
31085                 if (td) {
31086                     var bl = Roo.htmleditor.Block.factory(td);
31087                     bl.textAlign = cmd.replace('justify','');
31088                     bl.updateElement();
31089                     this.owner.fireEvent('editorevent', this);
31090                     return;
31091                 }
31092                 this.execCmd('styleWithCSS', true); // 
31093                 break;
31094             case 'bold':
31095             case 'italic':
31096                 // if there is no selection, then we insert, and set the curson inside it..
31097                 this.execCmd('styleWithCSS', false); 
31098                 break;
31099                 
31100         
31101             default:
31102                 break;
31103         }
31104         
31105         
31106         this.win.focus();
31107         this.execCmd(cmd, value);
31108         this.owner.fireEvent('editorevent', this);
31109         //this.updateToolbar();
31110         this.owner.deferFocus();
31111     },
31112
31113     /**
31114      * Executes a Midas editor command directly on the editor document.
31115      * For visual commands, you should use {@link #relayCmd} instead.
31116      * <b>This should only be called after the editor is initialized.</b>
31117      * @param {String} cmd The Midas command
31118      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31119      */
31120     execCmd : function(cmd, value){
31121         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31122         this.syncValue();
31123     },
31124  
31125  
31126    
31127     /**
31128      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31129      * to insert tRoo.
31130      * @param {String} text | dom node.. 
31131      */
31132     insertAtCursor : function(text)
31133     {
31134         
31135         if(!this.activated){
31136             return;
31137         }
31138          
31139         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31140             this.win.focus();
31141             
31142             
31143             // from jquery ui (MIT licenced)
31144             var range, node;
31145             var win = this.win;
31146             
31147             if (win.getSelection && win.getSelection().getRangeAt) {
31148                 
31149                 // delete the existing?
31150                 
31151                 this.createRange(this.getSelection()).deleteContents();
31152                 range = win.getSelection().getRangeAt(0);
31153                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31154                 range.insertNode(node);
31155                 range = range.cloneRange();
31156                 range.collapse(false);
31157                  
31158                 win.getSelection().removeAllRanges();
31159                 win.getSelection().addRange(range);
31160                 
31161                 
31162                 
31163             } else if (win.document.selection && win.document.selection.createRange) {
31164                 // no firefox support
31165                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31166                 win.document.selection.createRange().pasteHTML(txt);
31167             
31168             } else {
31169                 // no firefox support
31170                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31171                 this.execCmd('InsertHTML', txt);
31172             } 
31173             this.syncValue();
31174             
31175             this.deferFocus();
31176         }
31177     },
31178  // private
31179     mozKeyPress : function(e){
31180         if(e.ctrlKey){
31181             var c = e.getCharCode(), cmd;
31182           
31183             if(c > 0){
31184                 c = String.fromCharCode(c).toLowerCase();
31185                 switch(c){
31186                     case 'b':
31187                         cmd = 'bold';
31188                         break;
31189                     case 'i':
31190                         cmd = 'italic';
31191                         break;
31192                     
31193                     case 'u':
31194                         cmd = 'underline';
31195                         break;
31196                     
31197                     //case 'v':
31198                       //  this.cleanUpPaste.defer(100, this);
31199                       //  return;
31200                         
31201                 }
31202                 if(cmd){
31203                     
31204                     this.relayCmd(cmd);
31205                     //this.win.focus();
31206                     //this.execCmd(cmd);
31207                     //this.deferFocus();
31208                     e.preventDefault();
31209                 }
31210                 
31211             }
31212         }
31213     },
31214
31215     // private
31216     fixKeys : function(){ // load time branching for fastest keydown performance
31217         
31218         
31219         if(Roo.isIE){
31220             return function(e){
31221                 var k = e.getKey(), r;
31222                 if(k == e.TAB){
31223                     e.stopEvent();
31224                     r = this.doc.selection.createRange();
31225                     if(r){
31226                         r.collapse(true);
31227                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31228                         this.deferFocus();
31229                     }
31230                     return;
31231                 }
31232                 /// this is handled by Roo.htmleditor.KeyEnter
31233                  /*
31234                 if(k == e.ENTER){
31235                     r = this.doc.selection.createRange();
31236                     if(r){
31237                         var target = r.parentElement();
31238                         if(!target || target.tagName.toLowerCase() != 'li'){
31239                             e.stopEvent();
31240                             r.pasteHTML('<br/>');
31241                             r.collapse(false);
31242                             r.select();
31243                         }
31244                     }
31245                 }
31246                 */
31247                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31248                 //    this.cleanUpPaste.defer(100, this);
31249                 //    return;
31250                 //}
31251                 
31252                 
31253             };
31254         }else if(Roo.isOpera){
31255             return function(e){
31256                 var k = e.getKey();
31257                 if(k == e.TAB){
31258                     e.stopEvent();
31259                     this.win.focus();
31260                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31261                     this.deferFocus();
31262                 }
31263                
31264                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31265                 //    this.cleanUpPaste.defer(100, this);
31266                  //   return;
31267                 //}
31268                 
31269             };
31270         }else if(Roo.isSafari){
31271             return function(e){
31272                 var k = e.getKey();
31273                 
31274                 if(k == e.TAB){
31275                     e.stopEvent();
31276                     this.execCmd('InsertText','\t');
31277                     this.deferFocus();
31278                     return;
31279                 }
31280                  this.mozKeyPress(e);
31281                 
31282                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31283                  //   this.cleanUpPaste.defer(100, this);
31284                  //   return;
31285                // }
31286                 
31287              };
31288         }
31289     }(),
31290     
31291     getAllAncestors: function()
31292     {
31293         var p = this.getSelectedNode();
31294         var a = [];
31295         if (!p) {
31296             a.push(p); // push blank onto stack..
31297             p = this.getParentElement();
31298         }
31299         
31300         
31301         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31302             a.push(p);
31303             p = p.parentNode;
31304         }
31305         a.push(this.doc.body);
31306         return a;
31307     },
31308     lastSel : false,
31309     lastSelNode : false,
31310     
31311     
31312     getSelection : function() 
31313     {
31314         this.assignDocWin();
31315         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31316     },
31317     /**
31318      * Select a dom node
31319      * @param {DomElement} node the node to select
31320      */
31321     selectNode : function(node, collapse)
31322     {
31323         var nodeRange = node.ownerDocument.createRange();
31324         try {
31325             nodeRange.selectNode(node);
31326         } catch (e) {
31327             nodeRange.selectNodeContents(node);
31328         }
31329         if (collapse === true) {
31330             nodeRange.collapse(true);
31331         }
31332         //
31333         var s = this.win.getSelection();
31334         s.removeAllRanges();
31335         s.addRange(nodeRange);
31336     },
31337     
31338     getSelectedNode: function() 
31339     {
31340         // this may only work on Gecko!!!
31341         
31342         // should we cache this!!!!
31343         
31344          
31345          
31346         var range = this.createRange(this.getSelection()).cloneRange();
31347         
31348         if (Roo.isIE) {
31349             var parent = range.parentElement();
31350             while (true) {
31351                 var testRange = range.duplicate();
31352                 testRange.moveToElementText(parent);
31353                 if (testRange.inRange(range)) {
31354                     break;
31355                 }
31356                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31357                     break;
31358                 }
31359                 parent = parent.parentElement;
31360             }
31361             return parent;
31362         }
31363         
31364         // is ancestor a text element.
31365         var ac =  range.commonAncestorContainer;
31366         if (ac.nodeType == 3) {
31367             ac = ac.parentNode;
31368         }
31369         
31370         var ar = ac.childNodes;
31371          
31372         var nodes = [];
31373         var other_nodes = [];
31374         var has_other_nodes = false;
31375         for (var i=0;i<ar.length;i++) {
31376             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31377                 continue;
31378             }
31379             // fullly contained node.
31380             
31381             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31382                 nodes.push(ar[i]);
31383                 continue;
31384             }
31385             
31386             // probably selected..
31387             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31388                 other_nodes.push(ar[i]);
31389                 continue;
31390             }
31391             // outer..
31392             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31393                 continue;
31394             }
31395             
31396             
31397             has_other_nodes = true;
31398         }
31399         if (!nodes.length && other_nodes.length) {
31400             nodes= other_nodes;
31401         }
31402         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31403             return false;
31404         }
31405         
31406         return nodes[0];
31407     },
31408     
31409     
31410     createRange: function(sel)
31411     {
31412         // this has strange effects when using with 
31413         // top toolbar - not sure if it's a great idea.
31414         //this.editor.contentWindow.focus();
31415         if (typeof sel != "undefined") {
31416             try {
31417                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31418             } catch(e) {
31419                 return this.doc.createRange();
31420             }
31421         } else {
31422             return this.doc.createRange();
31423         }
31424     },
31425     getParentElement: function()
31426     {
31427         
31428         this.assignDocWin();
31429         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31430         
31431         var range = this.createRange(sel);
31432          
31433         try {
31434             var p = range.commonAncestorContainer;
31435             while (p.nodeType == 3) { // text node
31436                 p = p.parentNode;
31437             }
31438             return p;
31439         } catch (e) {
31440             return null;
31441         }
31442     
31443     },
31444     /***
31445      *
31446      * Range intersection.. the hard stuff...
31447      *  '-1' = before
31448      *  '0' = hits..
31449      *  '1' = after.
31450      *         [ -- selected range --- ]
31451      *   [fail]                        [fail]
31452      *
31453      *    basically..
31454      *      if end is before start or  hits it. fail.
31455      *      if start is after end or hits it fail.
31456      *
31457      *   if either hits (but other is outside. - then it's not 
31458      *   
31459      *    
31460      **/
31461     
31462     
31463     // @see http://www.thismuchiknow.co.uk/?p=64.
31464     rangeIntersectsNode : function(range, node)
31465     {
31466         var nodeRange = node.ownerDocument.createRange();
31467         try {
31468             nodeRange.selectNode(node);
31469         } catch (e) {
31470             nodeRange.selectNodeContents(node);
31471         }
31472     
31473         var rangeStartRange = range.cloneRange();
31474         rangeStartRange.collapse(true);
31475     
31476         var rangeEndRange = range.cloneRange();
31477         rangeEndRange.collapse(false);
31478     
31479         var nodeStartRange = nodeRange.cloneRange();
31480         nodeStartRange.collapse(true);
31481     
31482         var nodeEndRange = nodeRange.cloneRange();
31483         nodeEndRange.collapse(false);
31484     
31485         return rangeStartRange.compareBoundaryPoints(
31486                  Range.START_TO_START, nodeEndRange) == -1 &&
31487                rangeEndRange.compareBoundaryPoints(
31488                  Range.START_TO_START, nodeStartRange) == 1;
31489         
31490          
31491     },
31492     rangeCompareNode : function(range, node)
31493     {
31494         var nodeRange = node.ownerDocument.createRange();
31495         try {
31496             nodeRange.selectNode(node);
31497         } catch (e) {
31498             nodeRange.selectNodeContents(node);
31499         }
31500         
31501         
31502         range.collapse(true);
31503     
31504         nodeRange.collapse(true);
31505      
31506         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31507         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
31508          
31509         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31510         
31511         var nodeIsBefore   =  ss == 1;
31512         var nodeIsAfter    = ee == -1;
31513         
31514         if (nodeIsBefore && nodeIsAfter) {
31515             return 0; // outer
31516         }
31517         if (!nodeIsBefore && nodeIsAfter) {
31518             return 1; //right trailed.
31519         }
31520         
31521         if (nodeIsBefore && !nodeIsAfter) {
31522             return 2;  // left trailed.
31523         }
31524         // fully contined.
31525         return 3;
31526     },
31527  
31528     cleanWordChars : function(input) {// change the chars to hex code
31529         
31530        var swapCodes  = [ 
31531             [    8211, "&#8211;" ], 
31532             [    8212, "&#8212;" ], 
31533             [    8216,  "'" ],  
31534             [    8217, "'" ],  
31535             [    8220, '"' ],  
31536             [    8221, '"' ],  
31537             [    8226, "*" ],  
31538             [    8230, "..." ]
31539         ]; 
31540         var output = input;
31541         Roo.each(swapCodes, function(sw) { 
31542             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
31543             
31544             output = output.replace(swapper, sw[1]);
31545         });
31546         
31547         return output;
31548     },
31549     
31550      
31551     
31552         
31553     
31554     cleanUpChild : function (node)
31555     {
31556         
31557         new Roo.htmleditor.FilterComment({node : node});
31558         new Roo.htmleditor.FilterAttributes({
31559                 node : node,
31560                 attrib_black : this.ablack,
31561                 attrib_clean : this.aclean,
31562                 style_white : this.cwhite,
31563                 style_black : this.cblack
31564         });
31565         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
31566         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
31567          
31568         
31569     },
31570     
31571     /**
31572      * Clean up MS wordisms...
31573      * @deprecated - use filter directly
31574      */
31575     cleanWord : function(node)
31576     {
31577         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
31578         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
31579         
31580     },
31581    
31582     
31583     /**
31584
31585      * @deprecated - use filters
31586      */
31587     cleanTableWidths : function(node)
31588     {
31589         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
31590         
31591  
31592     },
31593     
31594      
31595         
31596     applyBlacklists : function()
31597     {
31598         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
31599         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
31600         
31601         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
31602         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
31603         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
31604         
31605         this.white = [];
31606         this.black = [];
31607         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
31608             if (b.indexOf(tag) > -1) {
31609                 return;
31610             }
31611             this.white.push(tag);
31612             
31613         }, this);
31614         
31615         Roo.each(w, function(tag) {
31616             if (b.indexOf(tag) > -1) {
31617                 return;
31618             }
31619             if (this.white.indexOf(tag) > -1) {
31620                 return;
31621             }
31622             this.white.push(tag);
31623             
31624         }, this);
31625         
31626         
31627         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
31628             if (w.indexOf(tag) > -1) {
31629                 return;
31630             }
31631             this.black.push(tag);
31632             
31633         }, this);
31634         
31635         Roo.each(b, function(tag) {
31636             if (w.indexOf(tag) > -1) {
31637                 return;
31638             }
31639             if (this.black.indexOf(tag) > -1) {
31640                 return;
31641             }
31642             this.black.push(tag);
31643             
31644         }, this);
31645         
31646         
31647         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
31648         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
31649         
31650         this.cwhite = [];
31651         this.cblack = [];
31652         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
31653             if (b.indexOf(tag) > -1) {
31654                 return;
31655             }
31656             this.cwhite.push(tag);
31657             
31658         }, this);
31659         
31660         Roo.each(w, function(tag) {
31661             if (b.indexOf(tag) > -1) {
31662                 return;
31663             }
31664             if (this.cwhite.indexOf(tag) > -1) {
31665                 return;
31666             }
31667             this.cwhite.push(tag);
31668             
31669         }, this);
31670         
31671         
31672         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
31673             if (w.indexOf(tag) > -1) {
31674                 return;
31675             }
31676             this.cblack.push(tag);
31677             
31678         }, this);
31679         
31680         Roo.each(b, function(tag) {
31681             if (w.indexOf(tag) > -1) {
31682                 return;
31683             }
31684             if (this.cblack.indexOf(tag) > -1) {
31685                 return;
31686             }
31687             this.cblack.push(tag);
31688             
31689         }, this);
31690     },
31691     
31692     setStylesheets : function(stylesheets)
31693     {
31694         if(typeof(stylesheets) == 'string'){
31695             Roo.get(this.iframe.contentDocument.head).createChild({
31696                 tag : 'link',
31697                 rel : 'stylesheet',
31698                 type : 'text/css',
31699                 href : stylesheets
31700             });
31701             
31702             return;
31703         }
31704         var _this = this;
31705      
31706         Roo.each(stylesheets, function(s) {
31707             if(!s.length){
31708                 return;
31709             }
31710             
31711             Roo.get(_this.iframe.contentDocument.head).createChild({
31712                 tag : 'link',
31713                 rel : 'stylesheet',
31714                 type : 'text/css',
31715                 href : s
31716             });
31717         });
31718
31719         
31720     },
31721     
31722     
31723     updateLanguage : function()
31724     {
31725         if (!this.iframe || !this.iframe.contentDocument) {
31726             return;
31727         }
31728         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
31729     },
31730     
31731     
31732     removeStylesheets : function()
31733     {
31734         var _this = this;
31735         
31736         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
31737             s.remove();
31738         });
31739     },
31740     
31741     setStyle : function(style)
31742     {
31743         Roo.get(this.iframe.contentDocument.head).createChild({
31744             tag : 'style',
31745             type : 'text/css',
31746             html : style
31747         });
31748
31749         return;
31750     }
31751     
31752     // hide stuff that is not compatible
31753     /**
31754      * @event blur
31755      * @hide
31756      */
31757     /**
31758      * @event change
31759      * @hide
31760      */
31761     /**
31762      * @event focus
31763      * @hide
31764      */
31765     /**
31766      * @event specialkey
31767      * @hide
31768      */
31769     /**
31770      * @cfg {String} fieldClass @hide
31771      */
31772     /**
31773      * @cfg {String} focusClass @hide
31774      */
31775     /**
31776      * @cfg {String} autoCreate @hide
31777      */
31778     /**
31779      * @cfg {String} inputType @hide
31780      */
31781     /**
31782      * @cfg {String} invalidClass @hide
31783      */
31784     /**
31785      * @cfg {String} invalidText @hide
31786      */
31787     /**
31788      * @cfg {String} msgFx @hide
31789      */
31790     /**
31791      * @cfg {String} validateOnBlur @hide
31792      */
31793 });
31794
31795 Roo.HtmlEditorCore.white = [
31796         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
31797         
31798        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
31799        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
31800        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
31801        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
31802        'TABLE',   'UL',         'XMP', 
31803        
31804        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
31805       'THEAD',   'TR', 
31806      
31807       'DIR', 'MENU', 'OL', 'UL', 'DL',
31808        
31809       'EMBED',  'OBJECT'
31810 ];
31811
31812
31813 Roo.HtmlEditorCore.black = [
31814     //    'embed',  'object', // enable - backend responsiblity to clean thiese
31815         'APPLET', // 
31816         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
31817         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
31818         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
31819         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
31820         //'FONT' // CLEAN LATER..
31821         'COLGROUP', 'COL'   // messy tables.
31822         
31823         
31824 ];
31825 Roo.HtmlEditorCore.clean = [ // ?? needed???
31826      'SCRIPT', 'STYLE', 'TITLE', 'XML'
31827 ];
31828 Roo.HtmlEditorCore.tag_remove = [
31829     'FONT', 'TBODY'  
31830 ];
31831 // attributes..
31832
31833 Roo.HtmlEditorCore.ablack = [
31834     'on'
31835 ];
31836     
31837 Roo.HtmlEditorCore.aclean = [ 
31838     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
31839 ];
31840
31841 // protocols..
31842 Roo.HtmlEditorCore.pwhite= [
31843         'http',  'https',  'mailto'
31844 ];
31845
31846 // white listed style attributes.
31847 Roo.HtmlEditorCore.cwhite= [
31848       //  'text-align', /// default is to allow most things..
31849       
31850          
31851 //        'font-size'//??
31852 ];
31853
31854 // black listed style attributes.
31855 Roo.HtmlEditorCore.cblack= [
31856       //  'font-size' -- this can be set by the project 
31857 ];
31858
31859
31860
31861
31862     /*
31863  * - LGPL
31864  *
31865  * HtmlEditor
31866  * 
31867  */
31868
31869 /**
31870  * @class Roo.bootstrap.form.HtmlEditor
31871  * @extends Roo.bootstrap.form.TextArea
31872  * Bootstrap HtmlEditor class
31873
31874  * @constructor
31875  * Create a new HtmlEditor
31876  * @param {Object} config The config object
31877  */
31878
31879 Roo.bootstrap.form.HtmlEditor = function(config){
31880     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
31881     if (!this.toolbars) {
31882         this.toolbars = [];
31883     }
31884     
31885     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
31886     this.addEvents({
31887             /**
31888              * @event initialize
31889              * Fires when the editor is fully initialized (including the iframe)
31890              * @param {HtmlEditor} this
31891              */
31892             initialize: true,
31893             /**
31894              * @event activate
31895              * Fires when the editor is first receives the focus. Any insertion must wait
31896              * until after this event.
31897              * @param {HtmlEditor} this
31898              */
31899             activate: true,
31900              /**
31901              * @event beforesync
31902              * Fires before the textarea is updated with content from the editor iframe. Return false
31903              * to cancel the sync.
31904              * @param {HtmlEditor} this
31905              * @param {String} html
31906              */
31907             beforesync: true,
31908              /**
31909              * @event beforepush
31910              * Fires before the iframe editor is updated with content from the textarea. Return false
31911              * to cancel the push.
31912              * @param {HtmlEditor} this
31913              * @param {String} html
31914              */
31915             beforepush: true,
31916              /**
31917              * @event sync
31918              * Fires when the textarea is updated with content from the editor iframe.
31919              * @param {HtmlEditor} this
31920              * @param {String} html
31921              */
31922             sync: true,
31923              /**
31924              * @event push
31925              * Fires when the iframe editor is updated with content from the textarea.
31926              * @param {HtmlEditor} this
31927              * @param {String} html
31928              */
31929             push: true,
31930              /**
31931              * @event editmodechange
31932              * Fires when the editor switches edit modes
31933              * @param {HtmlEditor} this
31934              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
31935              */
31936             editmodechange: true,
31937             /**
31938              * @event editorevent
31939              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31940              * @param {HtmlEditor} this
31941              */
31942             editorevent: true,
31943             /**
31944              * @event firstfocus
31945              * Fires when on first focus - needed by toolbars..
31946              * @param {HtmlEditor} this
31947              */
31948             firstfocus: true,
31949             /**
31950              * @event autosave
31951              * Auto save the htmlEditor value as a file into Events
31952              * @param {HtmlEditor} this
31953              */
31954             autosave: true,
31955             /**
31956              * @event savedpreview
31957              * preview the saved version of htmlEditor
31958              * @param {HtmlEditor} this
31959              */
31960             savedpreview: true
31961         });
31962 };
31963
31964
31965 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
31966     
31967     
31968       /**
31969      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
31970      */
31971     toolbars : false,
31972     
31973      /**
31974     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
31975     */
31976     btns : [],
31977    
31978      /**
31979      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
31980      *                        Roo.resizable.
31981      */
31982     resizable : false,
31983      /**
31984      * @cfg {Number} height (in pixels)
31985      */   
31986     height: 300,
31987    /**
31988      * @cfg {Number} width (in pixels)
31989      */   
31990     width: false,
31991     
31992     /**
31993      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
31994      * 
31995      */
31996     stylesheets: false,
31997     
31998     // id of frame..
31999     frameId: false,
32000     
32001     // private properties
32002     validationEvent : false,
32003     deferHeight: true,
32004     initialized : false,
32005     activated : false,
32006     
32007     onFocus : Roo.emptyFn,
32008     iframePad:3,
32009     hideMode:'offsets',
32010     
32011     tbContainer : false,
32012     
32013     bodyCls : '',
32014     
32015     toolbarContainer :function() {
32016         return this.wrap.select('.x-html-editor-tb',true).first();
32017     },
32018
32019     /**
32020      * Protected method that will not generally be called directly. It
32021      * is called when the editor creates its toolbar. Override this method if you need to
32022      * add custom toolbar buttons.
32023      * @param {HtmlEditor} editor
32024      */
32025     createToolbar : function(){
32026         Roo.log('renewing');
32027         Roo.log("create toolbars");
32028         
32029         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32030         this.toolbars[0].render(this.toolbarContainer());
32031         
32032         return;
32033         
32034 //        if (!editor.toolbars || !editor.toolbars.length) {
32035 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32036 //        }
32037 //        
32038 //        for (var i =0 ; i < editor.toolbars.length;i++) {
32039 //            editor.toolbars[i] = Roo.factory(
32040 //                    typeof(editor.toolbars[i]) == 'string' ?
32041 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
32042 //                Roo.bootstrap.form.HtmlEditor);
32043 //            editor.toolbars[i].init(editor);
32044 //        }
32045     },
32046
32047      
32048     // private
32049     onRender : function(ct, position)
32050     {
32051        // Roo.log("Call onRender: " + this.xtype);
32052         var _t = this;
32053         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32054       
32055         this.wrap = this.inputEl().wrap({
32056             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32057         });
32058         
32059         this.editorcore.onRender(ct, position);
32060          
32061         if (this.resizable) {
32062             this.resizeEl = new Roo.Resizable(this.wrap, {
32063                 pinned : true,
32064                 wrap: true,
32065                 dynamic : true,
32066                 minHeight : this.height,
32067                 height: this.height,
32068                 handles : this.resizable,
32069                 width: this.width,
32070                 listeners : {
32071                     resize : function(r, w, h) {
32072                         _t.onResize(w,h); // -something
32073                     }
32074                 }
32075             });
32076             
32077         }
32078         this.createToolbar(this);
32079        
32080         
32081         if(!this.width && this.resizable){
32082             this.setSize(this.wrap.getSize());
32083         }
32084         if (this.resizeEl) {
32085             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32086             // should trigger onReize..
32087         }
32088         
32089     },
32090
32091     // private
32092     onResize : function(w, h)
32093     {
32094         Roo.log('resize: ' +w + ',' + h );
32095         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32096         var ew = false;
32097         var eh = false;
32098         
32099         if(this.inputEl() ){
32100             if(typeof w == 'number'){
32101                 var aw = w - this.wrap.getFrameWidth('lr');
32102                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32103                 ew = aw;
32104             }
32105             if(typeof h == 'number'){
32106                  var tbh = -11;  // fixme it needs to tool bar size!
32107                 for (var i =0; i < this.toolbars.length;i++) {
32108                     // fixme - ask toolbars for heights?
32109                     tbh += this.toolbars[i].el.getHeight();
32110                     //if (this.toolbars[i].footer) {
32111                     //    tbh += this.toolbars[i].footer.el.getHeight();
32112                     //}
32113                 }
32114               
32115                 
32116                 
32117                 
32118                 
32119                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32120                 ah -= 5; // knock a few pixes off for look..
32121                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32122                 var eh = ah;
32123             }
32124         }
32125         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32126         this.editorcore.onResize(ew,eh);
32127         
32128     },
32129
32130     /**
32131      * Toggles the editor between standard and source edit mode.
32132      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32133      */
32134     toggleSourceEdit : function(sourceEditMode)
32135     {
32136         this.editorcore.toggleSourceEdit(sourceEditMode);
32137         
32138         if(this.editorcore.sourceEditMode){
32139             Roo.log('editor - showing textarea');
32140             
32141 //            Roo.log('in');
32142 //            Roo.log(this.syncValue());
32143             this.syncValue();
32144             this.inputEl().removeClass(['hide', 'x-hidden']);
32145             this.inputEl().dom.removeAttribute('tabIndex');
32146             this.inputEl().focus();
32147         }else{
32148             Roo.log('editor - hiding textarea');
32149 //            Roo.log('out')
32150 //            Roo.log(this.pushValue()); 
32151             this.pushValue();
32152             
32153             this.inputEl().addClass(['hide', 'x-hidden']);
32154             this.inputEl().dom.setAttribute('tabIndex', -1);
32155             //this.deferFocus();
32156         }
32157          
32158         if(this.resizable){
32159             this.setSize(this.wrap.getSize());
32160         }
32161         
32162         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32163     },
32164  
32165     // private (for BoxComponent)
32166     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32167
32168     // private (for BoxComponent)
32169     getResizeEl : function(){
32170         return this.wrap;
32171     },
32172
32173     // private (for BoxComponent)
32174     getPositionEl : function(){
32175         return this.wrap;
32176     },
32177
32178     // private
32179     initEvents : function(){
32180         this.originalValue = this.getValue();
32181     },
32182
32183 //    /**
32184 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32185 //     * @method
32186 //     */
32187 //    markInvalid : Roo.emptyFn,
32188 //    /**
32189 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32190 //     * @method
32191 //     */
32192 //    clearInvalid : Roo.emptyFn,
32193
32194     setValue : function(v){
32195         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32196         this.editorcore.pushValue();
32197     },
32198
32199      
32200     // private
32201     deferFocus : function(){
32202         this.focus.defer(10, this);
32203     },
32204
32205     // doc'ed in Field
32206     focus : function(){
32207         this.editorcore.focus();
32208         
32209     },
32210       
32211
32212     // private
32213     onDestroy : function(){
32214         
32215         
32216         
32217         if(this.rendered){
32218             
32219             for (var i =0; i < this.toolbars.length;i++) {
32220                 // fixme - ask toolbars for heights?
32221                 this.toolbars[i].onDestroy();
32222             }
32223             
32224             this.wrap.dom.innerHTML = '';
32225             this.wrap.remove();
32226         }
32227     },
32228
32229     // private
32230     onFirstFocus : function(){
32231         //Roo.log("onFirstFocus");
32232         this.editorcore.onFirstFocus();
32233          for (var i =0; i < this.toolbars.length;i++) {
32234             this.toolbars[i].onFirstFocus();
32235         }
32236         
32237     },
32238     
32239     // private
32240     syncValue : function()
32241     {   
32242         this.editorcore.syncValue();
32243     },
32244     
32245     pushValue : function()
32246     {   
32247         this.editorcore.pushValue();
32248     }
32249      
32250     
32251     // hide stuff that is not compatible
32252     /**
32253      * @event blur
32254      * @hide
32255      */
32256     /**
32257      * @event change
32258      * @hide
32259      */
32260     /**
32261      * @event focus
32262      * @hide
32263      */
32264     /**
32265      * @event specialkey
32266      * @hide
32267      */
32268     /**
32269      * @cfg {String} fieldClass @hide
32270      */
32271     /**
32272      * @cfg {String} focusClass @hide
32273      */
32274     /**
32275      * @cfg {String} autoCreate @hide
32276      */
32277     /**
32278      * @cfg {String} inputType @hide
32279      */
32280      
32281     /**
32282      * @cfg {String} invalidText @hide
32283      */
32284     /**
32285      * @cfg {String} msgFx @hide
32286      */
32287     /**
32288      * @cfg {String} validateOnBlur @hide
32289      */
32290 });
32291  
32292     
32293    
32294    
32295    
32296       
32297 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32298 /**
32299  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32300  * @parent Roo.bootstrap.form.HtmlEditor
32301  * @extends Roo.bootstrap.nav.Simplebar
32302  * Basic Toolbar
32303  * 
32304  * @example
32305  * Usage:
32306  *
32307  new Roo.bootstrap.form.HtmlEditor({
32308     ....
32309     toolbars : [
32310         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32311             disable : { fonts: 1 , format: 1, ..., ... , ...],
32312             btns : [ .... ]
32313         })
32314     }
32315      
32316  * 
32317  * @cfg {Object} disable List of elements to disable..
32318  * @cfg {Array} btns List of additional buttons.
32319  * 
32320  * 
32321  * NEEDS Extra CSS? 
32322  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32323  */
32324  
32325 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32326 {
32327     
32328     Roo.apply(this, config);
32329     
32330     // default disabled, based on 'good practice'..
32331     this.disable = this.disable || {};
32332     Roo.applyIf(this.disable, {
32333         fontSize : true,
32334         colors : true,
32335         specialElements : true
32336     });
32337     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32338     
32339     this.editor = config.editor;
32340     this.editorcore = config.editor.editorcore;
32341     
32342     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32343     
32344     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32345     // dont call parent... till later.
32346 }
32347 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32348      
32349     bar : true,
32350     
32351     editor : false,
32352     editorcore : false,
32353     
32354     
32355     formats : [
32356         "p" ,  
32357         "h1","h2","h3","h4","h5","h6", 
32358         "pre", "code", 
32359         "abbr", "acronym", "address", "cite", "samp", "var",
32360         'div','span'
32361     ],
32362     
32363     onRender : function(ct, position)
32364     {
32365        // Roo.log("Call onRender: " + this.xtype);
32366         
32367        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32368        Roo.log(this.el);
32369        this.el.dom.style.marginBottom = '0';
32370        var _this = this;
32371        var editorcore = this.editorcore;
32372        var editor= this.editor;
32373        
32374        var children = [];
32375        var btn = function(id,cmd , toggle, handler, html){
32376        
32377             var  event = toggle ? 'toggle' : 'click';
32378        
32379             var a = {
32380                 size : 'sm',
32381                 xtype: 'Button',
32382                 xns: Roo.bootstrap,
32383                 //glyphicon : id,
32384                 fa: id,
32385                 cmd : id || cmd,
32386                 enableToggle:toggle !== false,
32387                 html : html || '',
32388                 pressed : toggle ? false : null,
32389                 listeners : {}
32390             };
32391             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32392                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32393             };
32394             children.push(a);
32395             return a;
32396        }
32397        
32398     //    var cb_box = function...
32399         
32400         var style = {
32401                 xtype: 'Button',
32402                 size : 'sm',
32403                 xns: Roo.bootstrap,
32404                 fa : 'font',
32405                 //html : 'submit'
32406                 menu : {
32407                     xtype: 'Menu',
32408                     xns: Roo.bootstrap,
32409                     items:  []
32410                 }
32411         };
32412         Roo.each(this.formats, function(f) {
32413             style.menu.items.push({
32414                 xtype :'MenuItem',
32415                 xns: Roo.bootstrap,
32416                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32417                 tagname : f,
32418                 listeners : {
32419                     click : function()
32420                     {
32421                         editorcore.insertTag(this.tagname);
32422                         editor.focus();
32423                     }
32424                 }
32425                 
32426             });
32427         });
32428         children.push(style);   
32429         
32430         btn('bold',false,true);
32431         btn('italic',false,true);
32432         btn('align-left', 'justifyleft',true);
32433         btn('align-center', 'justifycenter',true);
32434         btn('align-right' , 'justifyright',true);
32435         btn('link', false, false, function(btn) {
32436             //Roo.log("create link?");
32437             var url = prompt(this.createLinkText, this.defaultLinkValue);
32438             if(url && url != 'http:/'+'/'){
32439                 this.editorcore.relayCmd('createlink', url);
32440             }
32441         }),
32442         btn('list','insertunorderedlist',true);
32443         btn('pencil', false,true, function(btn){
32444                 Roo.log(this);
32445                 this.toggleSourceEdit(btn.pressed);
32446         });
32447         
32448         if (this.editor.btns.length > 0) {
32449             for (var i = 0; i<this.editor.btns.length; i++) {
32450                 children.push(this.editor.btns[i]);
32451             }
32452         }
32453         
32454         /*
32455         var cog = {
32456                 xtype: 'Button',
32457                 size : 'sm',
32458                 xns: Roo.bootstrap,
32459                 glyphicon : 'cog',
32460                 //html : 'submit'
32461                 menu : {
32462                     xtype: 'Menu',
32463                     xns: Roo.bootstrap,
32464                     items:  []
32465                 }
32466         };
32467         
32468         cog.menu.items.push({
32469             xtype :'MenuItem',
32470             xns: Roo.bootstrap,
32471             html : Clean styles,
32472             tagname : f,
32473             listeners : {
32474                 click : function()
32475                 {
32476                     editorcore.insertTag(this.tagname);
32477                     editor.focus();
32478                 }
32479             }
32480             
32481         });
32482        */
32483         
32484          
32485        this.xtype = 'NavSimplebar';
32486         
32487         for(var i=0;i< children.length;i++) {
32488             
32489             this.buttons.add(this.addxtypeChild(children[i]));
32490             
32491         }
32492         
32493         editor.on('editorevent', this.updateToolbar, this);
32494     },
32495     onBtnClick : function(id)
32496     {
32497        this.editorcore.relayCmd(id);
32498        this.editorcore.focus();
32499     },
32500     
32501     /**
32502      * Protected method that will not generally be called directly. It triggers
32503      * a toolbar update by reading the markup state of the current selection in the editor.
32504      */
32505     updateToolbar: function(){
32506
32507         if(!this.editorcore.activated){
32508             this.editor.onFirstFocus(); // is this neeed?
32509             return;
32510         }
32511
32512         var btns = this.buttons; 
32513         var doc = this.editorcore.doc;
32514         btns.get('bold').setActive(doc.queryCommandState('bold'));
32515         btns.get('italic').setActive(doc.queryCommandState('italic'));
32516         //btns.get('underline').setActive(doc.queryCommandState('underline'));
32517         
32518         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32519         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32520         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32521         
32522         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32523         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32524          /*
32525         
32526         var ans = this.editorcore.getAllAncestors();
32527         if (this.formatCombo) {
32528             
32529             
32530             var store = this.formatCombo.store;
32531             this.formatCombo.setValue("");
32532             for (var i =0; i < ans.length;i++) {
32533                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32534                     // select it..
32535                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
32536                     break;
32537                 }
32538             }
32539         }
32540         
32541         
32542         
32543         // hides menus... - so this cant be on a menu...
32544         Roo.bootstrap.MenuMgr.hideAll();
32545         */
32546         Roo.bootstrap.menu.Manager.hideAll();
32547         //this.editorsyncValue();
32548     },
32549     onFirstFocus: function() {
32550         this.buttons.each(function(item){
32551            item.enable();
32552         });
32553     },
32554     toggleSourceEdit : function(sourceEditMode){
32555         
32556           
32557         if(sourceEditMode){
32558             Roo.log("disabling buttons");
32559            this.buttons.each( function(item){
32560                 if(item.cmd != 'pencil'){
32561                     item.disable();
32562                 }
32563             });
32564           
32565         }else{
32566             Roo.log("enabling buttons");
32567             if(this.editorcore.initialized){
32568                 this.buttons.each( function(item){
32569                     item.enable();
32570                 });
32571             }
32572             
32573         }
32574         Roo.log("calling toggole on editor");
32575         // tell the editor that it's been pressed..
32576         this.editor.toggleSourceEdit(sourceEditMode);
32577        
32578     }
32579 });
32580
32581
32582
32583
32584  
32585 /*
32586  * - LGPL
32587  */
32588
32589 /**
32590  * @class Roo.bootstrap.form.Markdown
32591  * @extends Roo.bootstrap.form.TextArea
32592  * Bootstrap Showdown editable area
32593  * @cfg {string} content
32594  * 
32595  * @constructor
32596  * Create a new Showdown
32597  */
32598
32599 Roo.bootstrap.form.Markdown = function(config){
32600     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
32601    
32602 };
32603
32604 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
32605     
32606     editing :false,
32607     
32608     initEvents : function()
32609     {
32610         
32611         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
32612         this.markdownEl = this.el.createChild({
32613             cls : 'roo-markdown-area'
32614         });
32615         this.inputEl().addClass('d-none');
32616         if (this.getValue() == '') {
32617             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32618             
32619         } else {
32620             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32621         }
32622         this.markdownEl.on('click', this.toggleTextEdit, this);
32623         this.on('blur', this.toggleTextEdit, this);
32624         this.on('specialkey', this.resizeTextArea, this);
32625     },
32626     
32627     toggleTextEdit : function()
32628     {
32629         var sh = this.markdownEl.getHeight();
32630         this.inputEl().addClass('d-none');
32631         this.markdownEl.addClass('d-none');
32632         if (!this.editing) {
32633             // show editor?
32634             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
32635             this.inputEl().removeClass('d-none');
32636             this.inputEl().focus();
32637             this.editing = true;
32638             return;
32639         }
32640         // show showdown...
32641         this.updateMarkdown();
32642         this.markdownEl.removeClass('d-none');
32643         this.editing = false;
32644         return;
32645     },
32646     updateMarkdown : function()
32647     {
32648         if (this.getValue() == '') {
32649             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32650             return;
32651         }
32652  
32653         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32654     },
32655     
32656     resizeTextArea: function () {
32657         
32658         var sh = 100;
32659         Roo.log([sh, this.getValue().split("\n").length * 30]);
32660         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
32661     },
32662     setValue : function(val)
32663     {
32664         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
32665         if (!this.editing) {
32666             this.updateMarkdown();
32667         }
32668         
32669     },
32670     focus : function()
32671     {
32672         if (!this.editing) {
32673             this.toggleTextEdit();
32674         }
32675         
32676     }
32677
32678
32679 });/*
32680  * Based on:
32681  * Ext JS Library 1.1.1
32682  * Copyright(c) 2006-2007, Ext JS, LLC.
32683  *
32684  * Originally Released Under LGPL - original licence link has changed is not relivant.
32685  *
32686  * Fork - LGPL
32687  * <script type="text/javascript">
32688  */
32689  
32690 /**
32691  * @class Roo.bootstrap.PagingToolbar
32692  * @extends Roo.bootstrap.nav.Simplebar
32693  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32694  * @constructor
32695  * Create a new PagingToolbar
32696  * @param {Object} config The config object
32697  * @param {Roo.data.Store} store
32698  */
32699 Roo.bootstrap.PagingToolbar = function(config)
32700 {
32701     // old args format still supported... - xtype is prefered..
32702         // created from xtype...
32703     
32704     this.ds = config.dataSource;
32705     
32706     if (config.store && !this.ds) {
32707         this.store= Roo.factory(config.store, Roo.data);
32708         this.ds = this.store;
32709         this.ds.xmodule = this.xmodule || false;
32710     }
32711     
32712     this.toolbarItems = [];
32713     if (config.items) {
32714         this.toolbarItems = config.items;
32715     }
32716     
32717     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
32718     
32719     this.cursor = 0;
32720     
32721     if (this.ds) { 
32722         this.bind(this.ds);
32723     }
32724     
32725     if (Roo.bootstrap.version == 4) {
32726         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
32727     } else {
32728         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
32729     }
32730     
32731 };
32732
32733 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
32734     /**
32735      * @cfg {Roo.bootstrap.Button} buttons[]
32736      * Buttons for the toolbar
32737      */
32738      /**
32739      * @cfg {Roo.data.Store} store
32740      * The underlying data store providing the paged data
32741      */
32742     /**
32743      * @cfg {String/HTMLElement/Element} container
32744      * container The id or element that will contain the toolbar
32745      */
32746     /**
32747      * @cfg {Boolean} displayInfo
32748      * True to display the displayMsg (defaults to false)
32749      */
32750     /**
32751      * @cfg {Number} pageSize
32752      * The number of records to display per page (defaults to 20)
32753      */
32754     pageSize: 20,
32755     /**
32756      * @cfg {String} displayMsg
32757      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32758      */
32759     displayMsg : 'Displaying {0} - {1} of {2}',
32760     /**
32761      * @cfg {String} emptyMsg
32762      * The message to display when no records are found (defaults to "No data to display")
32763      */
32764     emptyMsg : 'No data to display',
32765     /**
32766      * Customizable piece of the default paging text (defaults to "Page")
32767      * @type String
32768      */
32769     beforePageText : "Page",
32770     /**
32771      * Customizable piece of the default paging text (defaults to "of %0")
32772      * @type String
32773      */
32774     afterPageText : "of {0}",
32775     /**
32776      * Customizable piece of the default paging text (defaults to "First Page")
32777      * @type String
32778      */
32779     firstText : "First Page",
32780     /**
32781      * Customizable piece of the default paging text (defaults to "Previous Page")
32782      * @type String
32783      */
32784     prevText : "Previous Page",
32785     /**
32786      * Customizable piece of the default paging text (defaults to "Next Page")
32787      * @type String
32788      */
32789     nextText : "Next Page",
32790     /**
32791      * Customizable piece of the default paging text (defaults to "Last Page")
32792      * @type String
32793      */
32794     lastText : "Last Page",
32795     /**
32796      * Customizable piece of the default paging text (defaults to "Refresh")
32797      * @type String
32798      */
32799     refreshText : "Refresh",
32800
32801     buttons : false,
32802     // private
32803     onRender : function(ct, position) 
32804     {
32805         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
32806         this.navgroup.parentId = this.id;
32807         this.navgroup.onRender(this.el, null);
32808         // add the buttons to the navgroup
32809         
32810         if(this.displayInfo){
32811             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
32812             this.displayEl = this.el.select('.x-paging-info', true).first();
32813 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
32814 //            this.displayEl = navel.el.select('span',true).first();
32815         }
32816         
32817         var _this = this;
32818         
32819         if(this.buttons){
32820             Roo.each(_this.buttons, function(e){ // this might need to use render????
32821                Roo.factory(e).render(_this.el);
32822             });
32823         }
32824             
32825         Roo.each(_this.toolbarItems, function(e) {
32826             _this.navgroup.addItem(e);
32827         });
32828         
32829         
32830         this.first = this.navgroup.addItem({
32831             tooltip: this.firstText,
32832             cls: "prev btn-outline-secondary",
32833             html : ' <i class="fa fa-step-backward"></i>',
32834             disabled: true,
32835             preventDefault: true,
32836             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
32837         });
32838         
32839         this.prev =  this.navgroup.addItem({
32840             tooltip: this.prevText,
32841             cls: "prev btn-outline-secondary",
32842             html : ' <i class="fa fa-backward"></i>',
32843             disabled: true,
32844             preventDefault: true,
32845             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
32846         });
32847     //this.addSeparator();
32848         
32849         
32850         var field = this.navgroup.addItem( {
32851             tagtype : 'span',
32852             cls : 'x-paging-position  btn-outline-secondary',
32853              disabled: true,
32854             html : this.beforePageText  +
32855                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
32856                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
32857          } ); //?? escaped?
32858         
32859         this.field = field.el.select('input', true).first();
32860         this.field.on("keydown", this.onPagingKeydown, this);
32861         this.field.on("focus", function(){this.dom.select();});
32862     
32863     
32864         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
32865         //this.field.setHeight(18);
32866         //this.addSeparator();
32867         this.next = this.navgroup.addItem({
32868             tooltip: this.nextText,
32869             cls: "next btn-outline-secondary",
32870             html : ' <i class="fa fa-forward"></i>',
32871             disabled: true,
32872             preventDefault: true,
32873             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
32874         });
32875         this.last = this.navgroup.addItem({
32876             tooltip: this.lastText,
32877             html : ' <i class="fa fa-step-forward"></i>',
32878             cls: "next btn-outline-secondary",
32879             disabled: true,
32880             preventDefault: true,
32881             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
32882         });
32883     //this.addSeparator();
32884         this.loading = this.navgroup.addItem({
32885             tooltip: this.refreshText,
32886             cls: "btn-outline-secondary",
32887             html : ' <i class="fa fa-refresh"></i>',
32888             preventDefault: true,
32889             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
32890         });
32891         
32892     },
32893
32894     // private
32895     updateInfo : function(){
32896         if(this.displayEl){
32897             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
32898             var msg = count == 0 ?
32899                 this.emptyMsg :
32900                 String.format(
32901                     this.displayMsg,
32902                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32903                 );
32904             this.displayEl.update(msg);
32905         }
32906     },
32907
32908     // private
32909     onLoad : function(ds, r, o)
32910     {
32911         this.cursor = o.params && o.params.start ? o.params.start : 0;
32912         
32913         var d = this.getPageData(),
32914             ap = d.activePage,
32915             ps = d.pages;
32916         
32917         
32918         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
32919         this.field.dom.value = ap;
32920         this.first.setDisabled(ap == 1);
32921         this.prev.setDisabled(ap == 1);
32922         this.next.setDisabled(ap == ps);
32923         this.last.setDisabled(ap == ps);
32924         this.loading.enable();
32925         this.updateInfo();
32926     },
32927
32928     // private
32929     getPageData : function(){
32930         var total = this.ds.getTotalCount();
32931         return {
32932             total : total,
32933             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32934             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32935         };
32936     },
32937
32938     // private
32939     onLoadError : function(proxy, o){
32940         this.loading.enable();
32941         if (this.ds.events.loadexception.listeners.length  < 2) {
32942             // nothing has been assigned to loadexception except this...
32943             // so 
32944             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
32945
32946         }
32947     },
32948
32949     // private
32950     onPagingKeydown : function(e){
32951         var k = e.getKey();
32952         var d = this.getPageData();
32953         if(k == e.RETURN){
32954             var v = this.field.dom.value, pageNum;
32955             if(!v || isNaN(pageNum = parseInt(v, 10))){
32956                 this.field.dom.value = d.activePage;
32957                 return;
32958             }
32959             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32960             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32961             e.stopEvent();
32962         }
32963         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))
32964         {
32965           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32966           this.field.dom.value = pageNum;
32967           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32968           e.stopEvent();
32969         }
32970         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32971         {
32972           var v = this.field.dom.value, pageNum; 
32973           var increment = (e.shiftKey) ? 10 : 1;
32974           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32975                 increment *= -1;
32976           }
32977           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32978             this.field.dom.value = d.activePage;
32979             return;
32980           }
32981           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32982           {
32983             this.field.dom.value = parseInt(v, 10) + increment;
32984             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32985             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32986           }
32987           e.stopEvent();
32988         }
32989     },
32990
32991     // private
32992     beforeLoad : function(){
32993         if(this.loading){
32994             this.loading.disable();
32995         }
32996     },
32997
32998     // private
32999     onClick : function(which){
33000         
33001         var ds = this.ds;
33002         if (!ds) {
33003             return;
33004         }
33005         
33006         switch(which){
33007             case "first":
33008                 ds.load({params:{start: 0, limit: this.pageSize}});
33009             break;
33010             case "prev":
33011                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33012             break;
33013             case "next":
33014                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33015             break;
33016             case "last":
33017                 var total = ds.getTotalCount();
33018                 var extra = total % this.pageSize;
33019                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33020                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33021             break;
33022             case "refresh":
33023                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33024             break;
33025         }
33026     },
33027
33028     /**
33029      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33030      * @param {Roo.data.Store} store The data store to unbind
33031      */
33032     unbind : function(ds){
33033         ds.un("beforeload", this.beforeLoad, this);
33034         ds.un("load", this.onLoad, this);
33035         ds.un("loadexception", this.onLoadError, this);
33036         ds.un("remove", this.updateInfo, this);
33037         ds.un("add", this.updateInfo, this);
33038         this.ds = undefined;
33039     },
33040
33041     /**
33042      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33043      * @param {Roo.data.Store} store The data store to bind
33044      */
33045     bind : function(ds){
33046         ds.on("beforeload", this.beforeLoad, this);
33047         ds.on("load", this.onLoad, this);
33048         ds.on("loadexception", this.onLoadError, this);
33049         ds.on("remove", this.updateInfo, this);
33050         ds.on("add", this.updateInfo, this);
33051         this.ds = ds;
33052     }
33053 });/*
33054  * - LGPL
33055  *
33056  * element
33057  * 
33058  */
33059
33060 /**
33061  * @class Roo.bootstrap.MessageBar
33062  * @extends Roo.bootstrap.Component
33063  * Bootstrap MessageBar class
33064  * @cfg {String} html contents of the MessageBar
33065  * @cfg {String} weight (info | success | warning | danger) default info
33066  * @cfg {String} beforeClass insert the bar before the given class
33067  * @cfg {Boolean} closable (true | false) default false
33068  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33069  * 
33070  * @constructor
33071  * Create a new Element
33072  * @param {Object} config The config object
33073  */
33074
33075 Roo.bootstrap.MessageBar = function(config){
33076     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33077 };
33078
33079 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33080     
33081     html: '',
33082     weight: 'info',
33083     closable: false,
33084     fixed: false,
33085     beforeClass: 'bootstrap-sticky-wrap',
33086     
33087     getAutoCreate : function(){
33088         
33089         var cfg = {
33090             tag: 'div',
33091             cls: 'alert alert-dismissable alert-' + this.weight,
33092             cn: [
33093                 {
33094                     tag: 'span',
33095                     cls: 'message',
33096                     html: this.html || ''
33097                 }
33098             ]
33099         };
33100         
33101         if(this.fixed){
33102             cfg.cls += ' alert-messages-fixed';
33103         }
33104         
33105         if(this.closable){
33106             cfg.cn.push({
33107                 tag: 'button',
33108                 cls: 'close',
33109                 html: 'x'
33110             });
33111         }
33112         
33113         return cfg;
33114     },
33115     
33116     onRender : function(ct, position)
33117     {
33118         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33119         
33120         if(!this.el){
33121             var cfg = Roo.apply({},  this.getAutoCreate());
33122             cfg.id = Roo.id();
33123             
33124             if (this.cls) {
33125                 cfg.cls += ' ' + this.cls;
33126             }
33127             if (this.style) {
33128                 cfg.style = this.style;
33129             }
33130             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33131             
33132             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33133         }
33134         
33135         this.el.select('>button.close').on('click', this.hide, this);
33136         
33137     },
33138     
33139     show : function()
33140     {
33141         if (!this.rendered) {
33142             this.render();
33143         }
33144         
33145         this.el.show();
33146         
33147         this.fireEvent('show', this);
33148         
33149     },
33150     
33151     hide : function()
33152     {
33153         if (!this.rendered) {
33154             this.render();
33155         }
33156         
33157         this.el.hide();
33158         
33159         this.fireEvent('hide', this);
33160     },
33161     
33162     update : function()
33163     {
33164 //        var e = this.el.dom.firstChild;
33165 //        
33166 //        if(this.closable){
33167 //            e = e.nextSibling;
33168 //        }
33169 //        
33170 //        e.data = this.html || '';
33171
33172         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33173     }
33174    
33175 });
33176
33177  
33178
33179      /*
33180  * - LGPL
33181  *
33182  * Graph
33183  * 
33184  */
33185
33186
33187 /**
33188  * @class Roo.bootstrap.Graph
33189  * @extends Roo.bootstrap.Component
33190  * Bootstrap Graph class
33191 > Prameters
33192  -sm {number} sm 4
33193  -md {number} md 5
33194  @cfg {String} graphtype  bar | vbar | pie
33195  @cfg {number} g_x coodinator | centre x (pie)
33196  @cfg {number} g_y coodinator | centre y (pie)
33197  @cfg {number} g_r radius (pie)
33198  @cfg {number} g_height height of the chart (respected by all elements in the set)
33199  @cfg {number} g_width width of the chart (respected by all elements in the set)
33200  @cfg {Object} title The title of the chart
33201     
33202  -{Array}  values
33203  -opts (object) options for the chart 
33204      o {
33205      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33206      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33207      o vgutter (number)
33208      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.
33209      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33210      o to
33211      o stretch (boolean)
33212      o }
33213  -opts (object) options for the pie
33214      o{
33215      o cut
33216      o startAngle (number)
33217      o endAngle (number)
33218      } 
33219  *
33220  * @constructor
33221  * Create a new Input
33222  * @param {Object} config The config object
33223  */
33224
33225 Roo.bootstrap.Graph = function(config){
33226     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33227     
33228     this.addEvents({
33229         // img events
33230         /**
33231          * @event click
33232          * The img click event for the img.
33233          * @param {Roo.EventObject} e
33234          */
33235         "click" : true
33236     });
33237 };
33238
33239 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33240     
33241     sm: 4,
33242     md: 5,
33243     graphtype: 'bar',
33244     g_height: 250,
33245     g_width: 400,
33246     g_x: 50,
33247     g_y: 50,
33248     g_r: 30,
33249     opts:{
33250         //g_colors: this.colors,
33251         g_type: 'soft',
33252         g_gutter: '20%'
33253
33254     },
33255     title : false,
33256
33257     getAutoCreate : function(){
33258         
33259         var cfg = {
33260             tag: 'div',
33261             html : null
33262         };
33263         
33264         
33265         return  cfg;
33266     },
33267
33268     onRender : function(ct,position){
33269         
33270         
33271         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33272         
33273         if (typeof(Raphael) == 'undefined') {
33274             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33275             return;
33276         }
33277         
33278         this.raphael = Raphael(this.el.dom);
33279         
33280                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33281                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33282                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33283                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33284                 /*
33285                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33286                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33287                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33288                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33289                 
33290                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33291                 r.barchart(330, 10, 300, 220, data1);
33292                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33293                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33294                 */
33295                 
33296                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33297                 // r.barchart(30, 30, 560, 250,  xdata, {
33298                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33299                 //     axis : "0 0 1 1",
33300                 //     axisxlabels :  xdata
33301                 //     //yvalues : cols,
33302                    
33303                 // });
33304 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33305 //        
33306 //        this.load(null,xdata,{
33307 //                axis : "0 0 1 1",
33308 //                axisxlabels :  xdata
33309 //                });
33310
33311     },
33312
33313     load : function(graphtype,xdata,opts)
33314     {
33315         this.raphael.clear();
33316         if(!graphtype) {
33317             graphtype = this.graphtype;
33318         }
33319         if(!opts){
33320             opts = this.opts;
33321         }
33322         var r = this.raphael,
33323             fin = function () {
33324                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33325             },
33326             fout = function () {
33327                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33328             },
33329             pfin = function() {
33330                 this.sector.stop();
33331                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33332
33333                 if (this.label) {
33334                     this.label[0].stop();
33335                     this.label[0].attr({ r: 7.5 });
33336                     this.label[1].attr({ "font-weight": 800 });
33337                 }
33338             },
33339             pfout = function() {
33340                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33341
33342                 if (this.label) {
33343                     this.label[0].animate({ r: 5 }, 500, "bounce");
33344                     this.label[1].attr({ "font-weight": 400 });
33345                 }
33346             };
33347
33348         switch(graphtype){
33349             case 'bar':
33350                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33351                 break;
33352             case 'hbar':
33353                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33354                 break;
33355             case 'pie':
33356 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33357 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33358 //            
33359                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33360                 
33361                 break;
33362
33363         }
33364         
33365         if(this.title){
33366             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33367         }
33368         
33369     },
33370     
33371     setTitle: function(o)
33372     {
33373         this.title = o;
33374     },
33375     
33376     initEvents: function() {
33377         
33378         if(!this.href){
33379             this.el.on('click', this.onClick, this);
33380         }
33381     },
33382     
33383     onClick : function(e)
33384     {
33385         Roo.log('img onclick');
33386         this.fireEvent('click', this, e);
33387     }
33388    
33389 });
33390
33391  
33392 Roo.bootstrap.dash = {};/*
33393  * - LGPL
33394  *
33395  * numberBox
33396  * 
33397  */
33398 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33399
33400 /**
33401  * @class Roo.bootstrap.dash.NumberBox
33402  * @extends Roo.bootstrap.Component
33403  * Bootstrap NumberBox class
33404  * @cfg {String} headline Box headline
33405  * @cfg {String} content Box content
33406  * @cfg {String} icon Box icon
33407  * @cfg {String} footer Footer text
33408  * @cfg {String} fhref Footer href
33409  * 
33410  * @constructor
33411  * Create a new NumberBox
33412  * @param {Object} config The config object
33413  */
33414
33415
33416 Roo.bootstrap.dash.NumberBox = function(config){
33417     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33418     
33419 };
33420
33421 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33422     
33423     headline : '',
33424     content : '',
33425     icon : '',
33426     footer : '',
33427     fhref : '',
33428     ficon : '',
33429     
33430     getAutoCreate : function(){
33431         
33432         var cfg = {
33433             tag : 'div',
33434             cls : 'small-box ',
33435             cn : [
33436                 {
33437                     tag : 'div',
33438                     cls : 'inner',
33439                     cn :[
33440                         {
33441                             tag : 'h3',
33442                             cls : 'roo-headline',
33443                             html : this.headline
33444                         },
33445                         {
33446                             tag : 'p',
33447                             cls : 'roo-content',
33448                             html : this.content
33449                         }
33450                     ]
33451                 }
33452             ]
33453         };
33454         
33455         if(this.icon){
33456             cfg.cn.push({
33457                 tag : 'div',
33458                 cls : 'icon',
33459                 cn :[
33460                     {
33461                         tag : 'i',
33462                         cls : 'ion ' + this.icon
33463                     }
33464                 ]
33465             });
33466         }
33467         
33468         if(this.footer){
33469             var footer = {
33470                 tag : 'a',
33471                 cls : 'small-box-footer',
33472                 href : this.fhref || '#',
33473                 html : this.footer
33474             };
33475             
33476             cfg.cn.push(footer);
33477             
33478         }
33479         
33480         return  cfg;
33481     },
33482
33483     onRender : function(ct,position){
33484         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33485
33486
33487        
33488                 
33489     },
33490
33491     setHeadline: function (value)
33492     {
33493         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33494     },
33495     
33496     setFooter: function (value, href)
33497     {
33498         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33499         
33500         if(href){
33501             this.el.select('a.small-box-footer',true).first().attr('href', href);
33502         }
33503         
33504     },
33505
33506     setContent: function (value)
33507     {
33508         this.el.select('.roo-content',true).first().dom.innerHTML = value;
33509     },
33510
33511     initEvents: function() 
33512     {   
33513         
33514     }
33515     
33516 });
33517
33518  
33519 /*
33520  * - LGPL
33521  *
33522  * TabBox
33523  * 
33524  */
33525 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33526
33527 /**
33528  * @class Roo.bootstrap.dash.TabBox
33529  * @extends Roo.bootstrap.Component
33530  * @children Roo.bootstrap.dash.TabPane
33531  * Bootstrap TabBox class
33532  * @cfg {String} title Title of the TabBox
33533  * @cfg {String} icon Icon of the TabBox
33534  * @cfg {Boolean} showtabs (true|false) show the tabs default true
33535  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33536  * 
33537  * @constructor
33538  * Create a new TabBox
33539  * @param {Object} config The config object
33540  */
33541
33542
33543 Roo.bootstrap.dash.TabBox = function(config){
33544     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
33545     this.addEvents({
33546         // raw events
33547         /**
33548          * @event addpane
33549          * When a pane is added
33550          * @param {Roo.bootstrap.dash.TabPane} pane
33551          */
33552         "addpane" : true,
33553         /**
33554          * @event activatepane
33555          * When a pane is activated
33556          * @param {Roo.bootstrap.dash.TabPane} pane
33557          */
33558         "activatepane" : true
33559         
33560          
33561     });
33562     
33563     this.panes = [];
33564 };
33565
33566 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
33567
33568     title : '',
33569     icon : false,
33570     showtabs : true,
33571     tabScrollable : false,
33572     
33573     getChildContainer : function()
33574     {
33575         return this.el.select('.tab-content', true).first();
33576     },
33577     
33578     getAutoCreate : function(){
33579         
33580         var header = {
33581             tag: 'li',
33582             cls: 'pull-left header',
33583             html: this.title,
33584             cn : []
33585         };
33586         
33587         if(this.icon){
33588             header.cn.push({
33589                 tag: 'i',
33590                 cls: 'fa ' + this.icon
33591             });
33592         }
33593         
33594         var h = {
33595             tag: 'ul',
33596             cls: 'nav nav-tabs pull-right',
33597             cn: [
33598                 header
33599             ]
33600         };
33601         
33602         if(this.tabScrollable){
33603             h = {
33604                 tag: 'div',
33605                 cls: 'tab-header',
33606                 cn: [
33607                     {
33608                         tag: 'ul',
33609                         cls: 'nav nav-tabs pull-right',
33610                         cn: [
33611                             header
33612                         ]
33613                     }
33614                 ]
33615             };
33616         }
33617         
33618         var cfg = {
33619             tag: 'div',
33620             cls: 'nav-tabs-custom',
33621             cn: [
33622                 h,
33623                 {
33624                     tag: 'div',
33625                     cls: 'tab-content no-padding',
33626                     cn: []
33627                 }
33628             ]
33629         };
33630
33631         return  cfg;
33632     },
33633     initEvents : function()
33634     {
33635         //Roo.log('add add pane handler');
33636         this.on('addpane', this.onAddPane, this);
33637     },
33638      /**
33639      * Updates the box title
33640      * @param {String} html to set the title to.
33641      */
33642     setTitle : function(value)
33643     {
33644         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
33645     },
33646     onAddPane : function(pane)
33647     {
33648         this.panes.push(pane);
33649         //Roo.log('addpane');
33650         //Roo.log(pane);
33651         // tabs are rendere left to right..
33652         if(!this.showtabs){
33653             return;
33654         }
33655         
33656         var ctr = this.el.select('.nav-tabs', true).first();
33657          
33658          
33659         var existing = ctr.select('.nav-tab',true);
33660         var qty = existing.getCount();;
33661         
33662         
33663         var tab = ctr.createChild({
33664             tag : 'li',
33665             cls : 'nav-tab' + (qty ? '' : ' active'),
33666             cn : [
33667                 {
33668                     tag : 'a',
33669                     href:'#',
33670                     html : pane.title
33671                 }
33672             ]
33673         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
33674         pane.tab = tab;
33675         
33676         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
33677         if (!qty) {
33678             pane.el.addClass('active');
33679         }
33680         
33681                 
33682     },
33683     onTabClick : function(ev,un,ob,pane)
33684     {
33685         //Roo.log('tab - prev default');
33686         ev.preventDefault();
33687         
33688         
33689         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
33690         pane.tab.addClass('active');
33691         //Roo.log(pane.title);
33692         this.getChildContainer().select('.tab-pane',true).removeClass('active');
33693         // technically we should have a deactivate event.. but maybe add later.
33694         // and it should not de-activate the selected tab...
33695         this.fireEvent('activatepane', pane);
33696         pane.el.addClass('active');
33697         pane.fireEvent('activate');
33698         
33699         
33700     },
33701     
33702     getActivePane : function()
33703     {
33704         var r = false;
33705         Roo.each(this.panes, function(p) {
33706             if(p.el.hasClass('active')){
33707                 r = p;
33708                 return false;
33709             }
33710             
33711             return;
33712         });
33713         
33714         return r;
33715     }
33716     
33717     
33718 });
33719
33720  
33721 /*
33722  * - LGPL
33723  *
33724  * Tab pane
33725  * 
33726  */
33727 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33728 /**
33729  * @class Roo.bootstrap.TabPane
33730  * @extends Roo.bootstrap.Component
33731  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
33732  * Bootstrap TabPane class
33733  * @cfg {Boolean} active (false | true) Default false
33734  * @cfg {String} title title of panel
33735
33736  * 
33737  * @constructor
33738  * Create a new TabPane
33739  * @param {Object} config The config object
33740  */
33741
33742 Roo.bootstrap.dash.TabPane = function(config){
33743     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
33744     
33745     this.addEvents({
33746         // raw events
33747         /**
33748          * @event activate
33749          * When a pane is activated
33750          * @param {Roo.bootstrap.dash.TabPane} pane
33751          */
33752         "activate" : true
33753          
33754     });
33755 };
33756
33757 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
33758     
33759     active : false,
33760     title : '',
33761     
33762     // the tabBox that this is attached to.
33763     tab : false,
33764      
33765     getAutoCreate : function() 
33766     {
33767         var cfg = {
33768             tag: 'div',
33769             cls: 'tab-pane'
33770         };
33771         
33772         if(this.active){
33773             cfg.cls += ' active';
33774         }
33775         
33776         return cfg;
33777     },
33778     initEvents  : function()
33779     {
33780         //Roo.log('trigger add pane handler');
33781         this.parent().fireEvent('addpane', this)
33782     },
33783     
33784      /**
33785      * Updates the tab title 
33786      * @param {String} html to set the title to.
33787      */
33788     setTitle: function(str)
33789     {
33790         if (!this.tab) {
33791             return;
33792         }
33793         this.title = str;
33794         this.tab.select('a', true).first().dom.innerHTML = str;
33795         
33796     }
33797     
33798     
33799     
33800 });
33801
33802  
33803
33804
33805  /*
33806  * - LGPL
33807  *
33808  * Tooltip
33809  * 
33810  */
33811
33812 /**
33813  * @class Roo.bootstrap.Tooltip
33814  * Bootstrap Tooltip class
33815  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
33816  * to determine which dom element triggers the tooltip.
33817  * 
33818  * It needs to add support for additional attributes like tooltip-position
33819  * 
33820  * @constructor
33821  * Create a new Toolti
33822  * @param {Object} config The config object
33823  */
33824
33825 Roo.bootstrap.Tooltip = function(config){
33826     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
33827     
33828     this.alignment = Roo.bootstrap.Tooltip.alignment;
33829     
33830     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
33831         this.alignment = config.alignment;
33832     }
33833     
33834 };
33835
33836 Roo.apply(Roo.bootstrap.Tooltip, {
33837     /**
33838      * @function init initialize tooltip monitoring.
33839      * @static
33840      */
33841     currentEl : false,
33842     currentTip : false,
33843     currentRegion : false,
33844     
33845     //  init : delay?
33846     
33847     init : function()
33848     {
33849         Roo.get(document).on('mouseover', this.enter ,this);
33850         Roo.get(document).on('mouseout', this.leave, this);
33851          
33852         
33853         this.currentTip = new Roo.bootstrap.Tooltip();
33854     },
33855     
33856     enter : function(ev)
33857     {
33858         var dom = ev.getTarget();
33859         
33860         //Roo.log(['enter',dom]);
33861         var el = Roo.fly(dom);
33862         if (this.currentEl) {
33863             //Roo.log(dom);
33864             //Roo.log(this.currentEl);
33865             //Roo.log(this.currentEl.contains(dom));
33866             if (this.currentEl == el) {
33867                 return;
33868             }
33869             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
33870                 return;
33871             }
33872
33873         }
33874         
33875         if (this.currentTip.el) {
33876             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
33877         }    
33878         //Roo.log(ev);
33879         
33880         if(!el || el.dom == document){
33881             return;
33882         }
33883         
33884         var bindEl = el; 
33885         var pel = false;
33886         if (!el.attr('tooltip')) {
33887             pel = el.findParent("[tooltip]");
33888             if (pel) {
33889                 bindEl = Roo.get(pel);
33890             }
33891         }
33892         
33893        
33894         
33895         // you can not look for children, as if el is the body.. then everythign is the child..
33896         if (!pel && !el.attr('tooltip')) { //
33897             if (!el.select("[tooltip]").elements.length) {
33898                 return;
33899             }
33900             // is the mouse over this child...?
33901             bindEl = el.select("[tooltip]").first();
33902             var xy = ev.getXY();
33903             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
33904                 //Roo.log("not in region.");
33905                 return;
33906             }
33907             //Roo.log("child element over..");
33908             
33909         }
33910         this.currentEl = el;
33911         this.currentTip.bind(bindEl);
33912         this.currentRegion = Roo.lib.Region.getRegion(dom);
33913         this.currentTip.enter();
33914         
33915     },
33916     leave : function(ev)
33917     {
33918         var dom = ev.getTarget();
33919         //Roo.log(['leave',dom]);
33920         if (!this.currentEl) {
33921             return;
33922         }
33923         
33924         
33925         if (dom != this.currentEl.dom) {
33926             return;
33927         }
33928         var xy = ev.getXY();
33929         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
33930             return;
33931         }
33932         // only activate leave if mouse cursor is outside... bounding box..
33933         
33934         
33935         
33936         
33937         if (this.currentTip) {
33938             this.currentTip.leave();
33939         }
33940         //Roo.log('clear currentEl');
33941         this.currentEl = false;
33942         
33943         
33944     },
33945     alignment : {
33946         'left' : ['r-l', [-2,0], 'right'],
33947         'right' : ['l-r', [2,0], 'left'],
33948         'bottom' : ['t-b', [0,2], 'top'],
33949         'top' : [ 'b-t', [0,-2], 'bottom']
33950     }
33951     
33952 });
33953
33954
33955 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
33956     
33957     
33958     bindEl : false,
33959     
33960     delay : null, // can be { show : 300 , hide: 500}
33961     
33962     timeout : null,
33963     
33964     hoverState : null, //???
33965     
33966     placement : 'bottom', 
33967     
33968     alignment : false,
33969     
33970     getAutoCreate : function(){
33971     
33972         var cfg = {
33973            cls : 'tooltip',   
33974            role : 'tooltip',
33975            cn : [
33976                 {
33977                     cls : 'tooltip-arrow arrow'
33978                 },
33979                 {
33980                     cls : 'tooltip-inner'
33981                 }
33982            ]
33983         };
33984         
33985         return cfg;
33986     },
33987     bind : function(el)
33988     {
33989         this.bindEl = el;
33990     },
33991     
33992     initEvents : function()
33993     {
33994         this.arrowEl = this.el.select('.arrow', true).first();
33995         this.innerEl = this.el.select('.tooltip-inner', true).first();
33996     },
33997     
33998     enter : function () {
33999        
34000         if (this.timeout != null) {
34001             clearTimeout(this.timeout);
34002         }
34003         
34004         this.hoverState = 'in';
34005          //Roo.log("enter - show");
34006         if (!this.delay || !this.delay.show) {
34007             this.show();
34008             return;
34009         }
34010         var _t = this;
34011         this.timeout = setTimeout(function () {
34012             if (_t.hoverState == 'in') {
34013                 _t.show();
34014             }
34015         }, this.delay.show);
34016     },
34017     leave : function()
34018     {
34019         clearTimeout(this.timeout);
34020     
34021         this.hoverState = 'out';
34022          if (!this.delay || !this.delay.hide) {
34023             this.hide();
34024             return;
34025         }
34026        
34027         var _t = this;
34028         this.timeout = setTimeout(function () {
34029             //Roo.log("leave - timeout");
34030             
34031             if (_t.hoverState == 'out') {
34032                 _t.hide();
34033                 Roo.bootstrap.Tooltip.currentEl = false;
34034             }
34035         }, delay);
34036     },
34037     
34038     show : function (msg)
34039     {
34040         if (!this.el) {
34041             this.render(document.body);
34042         }
34043         // set content.
34044         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34045         
34046         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34047         
34048         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34049         
34050         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34051                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34052
34053         if(this.bindEl.attr('tooltip-class')) {
34054             this.el.addClass(this.bindEl.attr('tooltip-class'));
34055         }
34056         
34057         var placement = typeof this.placement == 'function' ?
34058             this.placement.call(this, this.el, on_el) :
34059             this.placement;
34060         
34061         if(this.bindEl.attr('tooltip-placement')) {
34062             placement = this.bindEl.attr('tooltip-placement');
34063         }
34064             
34065         var autoToken = /\s?auto?\s?/i;
34066         var autoPlace = autoToken.test(placement);
34067         if (autoPlace) {
34068             placement = placement.replace(autoToken, '') || 'top';
34069         }
34070         
34071         //this.el.detach()
34072         //this.el.setXY([0,0]);
34073         this.el.show();
34074         //this.el.dom.style.display='block';
34075         
34076         //this.el.appendTo(on_el);
34077         
34078         var p = this.getPosition();
34079         var box = this.el.getBox();
34080         
34081         if (autoPlace) {
34082             // fixme..
34083         }
34084         
34085         var align = this.alignment[placement];
34086         
34087         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34088         
34089         if(placement == 'top' || placement == 'bottom'){
34090             if(xy[0] < 0){
34091                 placement = 'right';
34092             }
34093             
34094             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34095                 placement = 'left';
34096             }
34097             
34098             var scroll = Roo.select('body', true).first().getScroll();
34099             
34100             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34101                 placement = 'top';
34102             }
34103             
34104             align = this.alignment[placement];
34105             
34106             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34107             
34108         }
34109         
34110         var elems = document.getElementsByTagName('div');
34111         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34112         for (var i = 0; i < elems.length; i++) {
34113           var zindex = Number.parseInt(
34114                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34115                 10
34116           );
34117           if (zindex > highest) {
34118             highest = zindex;
34119           }
34120         }
34121         
34122         
34123         
34124         this.el.dom.style.zIndex = highest;
34125         
34126         this.el.alignTo(this.bindEl, align[0],align[1]);
34127         //var arrow = this.el.select('.arrow',true).first();
34128         //arrow.set(align[2], 
34129         
34130         this.el.addClass(placement);
34131         this.el.addClass("bs-tooltip-"+ placement);
34132         
34133         this.el.addClass('in fade show');
34134         
34135         this.hoverState = null;
34136         
34137         if (this.el.hasClass('fade')) {
34138             // fade it?
34139         }
34140         
34141         
34142         
34143         
34144         
34145     },
34146     hide : function()
34147     {
34148          
34149         if (!this.el) {
34150             return;
34151         }
34152         //this.el.setXY([0,0]);
34153         if(this.bindEl.attr('tooltip-class')) {
34154             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34155         }
34156         this.el.removeClass(['show', 'in']);
34157         //this.el.hide();
34158         
34159     }
34160     
34161 });
34162  
34163
34164  /*
34165  * - LGPL
34166  *
34167  * Location Picker
34168  * 
34169  */
34170
34171 /**
34172  * @class Roo.bootstrap.LocationPicker
34173  * @extends Roo.bootstrap.Component
34174  * Bootstrap LocationPicker class
34175  * @cfg {Number} latitude Position when init default 0
34176  * @cfg {Number} longitude Position when init default 0
34177  * @cfg {Number} zoom default 15
34178  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34179  * @cfg {Boolean} mapTypeControl default false
34180  * @cfg {Boolean} disableDoubleClickZoom default false
34181  * @cfg {Boolean} scrollwheel default true
34182  * @cfg {Boolean} streetViewControl default false
34183  * @cfg {Number} radius default 0
34184  * @cfg {String} locationName
34185  * @cfg {Boolean} draggable default true
34186  * @cfg {Boolean} enableAutocomplete default false
34187  * @cfg {Boolean} enableReverseGeocode default true
34188  * @cfg {String} markerTitle
34189  * 
34190  * @constructor
34191  * Create a new LocationPicker
34192  * @param {Object} config The config object
34193  */
34194
34195
34196 Roo.bootstrap.LocationPicker = function(config){
34197     
34198     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34199     
34200     this.addEvents({
34201         /**
34202          * @event initial
34203          * Fires when the picker initialized.
34204          * @param {Roo.bootstrap.LocationPicker} this
34205          * @param {Google Location} location
34206          */
34207         initial : true,
34208         /**
34209          * @event positionchanged
34210          * Fires when the picker position changed.
34211          * @param {Roo.bootstrap.LocationPicker} this
34212          * @param {Google Location} location
34213          */
34214         positionchanged : true,
34215         /**
34216          * @event resize
34217          * Fires when the map resize.
34218          * @param {Roo.bootstrap.LocationPicker} this
34219          */
34220         resize : true,
34221         /**
34222          * @event show
34223          * Fires when the map show.
34224          * @param {Roo.bootstrap.LocationPicker} this
34225          */
34226         show : true,
34227         /**
34228          * @event hide
34229          * Fires when the map hide.
34230          * @param {Roo.bootstrap.LocationPicker} this
34231          */
34232         hide : true,
34233         /**
34234          * @event mapClick
34235          * Fires when click the map.
34236          * @param {Roo.bootstrap.LocationPicker} this
34237          * @param {Map event} e
34238          */
34239         mapClick : true,
34240         /**
34241          * @event mapRightClick
34242          * Fires when right click the map.
34243          * @param {Roo.bootstrap.LocationPicker} this
34244          * @param {Map event} e
34245          */
34246         mapRightClick : true,
34247         /**
34248          * @event markerClick
34249          * Fires when click the marker.
34250          * @param {Roo.bootstrap.LocationPicker} this
34251          * @param {Map event} e
34252          */
34253         markerClick : true,
34254         /**
34255          * @event markerRightClick
34256          * Fires when right click the marker.
34257          * @param {Roo.bootstrap.LocationPicker} this
34258          * @param {Map event} e
34259          */
34260         markerRightClick : true,
34261         /**
34262          * @event OverlayViewDraw
34263          * Fires when OverlayView Draw
34264          * @param {Roo.bootstrap.LocationPicker} this
34265          */
34266         OverlayViewDraw : true,
34267         /**
34268          * @event OverlayViewOnAdd
34269          * Fires when OverlayView Draw
34270          * @param {Roo.bootstrap.LocationPicker} this
34271          */
34272         OverlayViewOnAdd : true,
34273         /**
34274          * @event OverlayViewOnRemove
34275          * Fires when OverlayView Draw
34276          * @param {Roo.bootstrap.LocationPicker} this
34277          */
34278         OverlayViewOnRemove : true,
34279         /**
34280          * @event OverlayViewShow
34281          * Fires when OverlayView Draw
34282          * @param {Roo.bootstrap.LocationPicker} this
34283          * @param {Pixel} cpx
34284          */
34285         OverlayViewShow : true,
34286         /**
34287          * @event OverlayViewHide
34288          * Fires when OverlayView Draw
34289          * @param {Roo.bootstrap.LocationPicker} this
34290          */
34291         OverlayViewHide : true,
34292         /**
34293          * @event loadexception
34294          * Fires when load google lib failed.
34295          * @param {Roo.bootstrap.LocationPicker} this
34296          */
34297         loadexception : true
34298     });
34299         
34300 };
34301
34302 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34303     
34304     gMapContext: false,
34305     
34306     latitude: 0,
34307     longitude: 0,
34308     zoom: 15,
34309     mapTypeId: false,
34310     mapTypeControl: false,
34311     disableDoubleClickZoom: false,
34312     scrollwheel: true,
34313     streetViewControl: false,
34314     radius: 0,
34315     locationName: '',
34316     draggable: true,
34317     enableAutocomplete: false,
34318     enableReverseGeocode: true,
34319     markerTitle: '',
34320     
34321     getAutoCreate: function()
34322     {
34323
34324         var cfg = {
34325             tag: 'div',
34326             cls: 'roo-location-picker'
34327         };
34328         
34329         return cfg
34330     },
34331     
34332     initEvents: function(ct, position)
34333     {       
34334         if(!this.el.getWidth() || this.isApplied()){
34335             return;
34336         }
34337         
34338         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34339         
34340         this.initial();
34341     },
34342     
34343     initial: function()
34344     {
34345         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34346             this.fireEvent('loadexception', this);
34347             return;
34348         }
34349         
34350         if(!this.mapTypeId){
34351             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34352         }
34353         
34354         this.gMapContext = this.GMapContext();
34355         
34356         this.initOverlayView();
34357         
34358         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34359         
34360         var _this = this;
34361                 
34362         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34363             _this.setPosition(_this.gMapContext.marker.position);
34364         });
34365         
34366         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34367             _this.fireEvent('mapClick', this, event);
34368             
34369         });
34370
34371         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34372             _this.fireEvent('mapRightClick', this, event);
34373             
34374         });
34375         
34376         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34377             _this.fireEvent('markerClick', this, event);
34378             
34379         });
34380
34381         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34382             _this.fireEvent('markerRightClick', this, event);
34383             
34384         });
34385         
34386         this.setPosition(this.gMapContext.location);
34387         
34388         this.fireEvent('initial', this, this.gMapContext.location);
34389     },
34390     
34391     initOverlayView: function()
34392     {
34393         var _this = this;
34394         
34395         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34396             
34397             draw: function()
34398             {
34399                 _this.fireEvent('OverlayViewDraw', _this);
34400             },
34401             
34402             onAdd: function()
34403             {
34404                 _this.fireEvent('OverlayViewOnAdd', _this);
34405             },
34406             
34407             onRemove: function()
34408             {
34409                 _this.fireEvent('OverlayViewOnRemove', _this);
34410             },
34411             
34412             show: function(cpx)
34413             {
34414                 _this.fireEvent('OverlayViewShow', _this, cpx);
34415             },
34416             
34417             hide: function()
34418             {
34419                 _this.fireEvent('OverlayViewHide', _this);
34420             }
34421             
34422         });
34423     },
34424     
34425     fromLatLngToContainerPixel: function(event)
34426     {
34427         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34428     },
34429     
34430     isApplied: function() 
34431     {
34432         return this.getGmapContext() == false ? false : true;
34433     },
34434     
34435     getGmapContext: function() 
34436     {
34437         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34438     },
34439     
34440     GMapContext: function() 
34441     {
34442         var position = new google.maps.LatLng(this.latitude, this.longitude);
34443         
34444         var _map = new google.maps.Map(this.el.dom, {
34445             center: position,
34446             zoom: this.zoom,
34447             mapTypeId: this.mapTypeId,
34448             mapTypeControl: this.mapTypeControl,
34449             disableDoubleClickZoom: this.disableDoubleClickZoom,
34450             scrollwheel: this.scrollwheel,
34451             streetViewControl: this.streetViewControl,
34452             locationName: this.locationName,
34453             draggable: this.draggable,
34454             enableAutocomplete: this.enableAutocomplete,
34455             enableReverseGeocode: this.enableReverseGeocode
34456         });
34457         
34458         var _marker = new google.maps.Marker({
34459             position: position,
34460             map: _map,
34461             title: this.markerTitle,
34462             draggable: this.draggable
34463         });
34464         
34465         return {
34466             map: _map,
34467             marker: _marker,
34468             circle: null,
34469             location: position,
34470             radius: this.radius,
34471             locationName: this.locationName,
34472             addressComponents: {
34473                 formatted_address: null,
34474                 addressLine1: null,
34475                 addressLine2: null,
34476                 streetName: null,
34477                 streetNumber: null,
34478                 city: null,
34479                 district: null,
34480                 state: null,
34481                 stateOrProvince: null
34482             },
34483             settings: this,
34484             domContainer: this.el.dom,
34485             geodecoder: new google.maps.Geocoder()
34486         };
34487     },
34488     
34489     drawCircle: function(center, radius, options) 
34490     {
34491         if (this.gMapContext.circle != null) {
34492             this.gMapContext.circle.setMap(null);
34493         }
34494         if (radius > 0) {
34495             radius *= 1;
34496             options = Roo.apply({}, options, {
34497                 strokeColor: "#0000FF",
34498                 strokeOpacity: .35,
34499                 strokeWeight: 2,
34500                 fillColor: "#0000FF",
34501                 fillOpacity: .2
34502             });
34503             
34504             options.map = this.gMapContext.map;
34505             options.radius = radius;
34506             options.center = center;
34507             this.gMapContext.circle = new google.maps.Circle(options);
34508             return this.gMapContext.circle;
34509         }
34510         
34511         return null;
34512     },
34513     
34514     setPosition: function(location) 
34515     {
34516         this.gMapContext.location = location;
34517         this.gMapContext.marker.setPosition(location);
34518         this.gMapContext.map.panTo(location);
34519         this.drawCircle(location, this.gMapContext.radius, {});
34520         
34521         var _this = this;
34522         
34523         if (this.gMapContext.settings.enableReverseGeocode) {
34524             this.gMapContext.geodecoder.geocode({
34525                 latLng: this.gMapContext.location
34526             }, function(results, status) {
34527                 
34528                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34529                     _this.gMapContext.locationName = results[0].formatted_address;
34530                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34531                     
34532                     _this.fireEvent('positionchanged', this, location);
34533                 }
34534             });
34535             
34536             return;
34537         }
34538         
34539         this.fireEvent('positionchanged', this, location);
34540     },
34541     
34542     resize: function()
34543     {
34544         google.maps.event.trigger(this.gMapContext.map, "resize");
34545         
34546         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
34547         
34548         this.fireEvent('resize', this);
34549     },
34550     
34551     setPositionByLatLng: function(latitude, longitude)
34552     {
34553         this.setPosition(new google.maps.LatLng(latitude, longitude));
34554     },
34555     
34556     getCurrentPosition: function() 
34557     {
34558         return {
34559             latitude: this.gMapContext.location.lat(),
34560             longitude: this.gMapContext.location.lng()
34561         };
34562     },
34563     
34564     getAddressName: function() 
34565     {
34566         return this.gMapContext.locationName;
34567     },
34568     
34569     getAddressComponents: function() 
34570     {
34571         return this.gMapContext.addressComponents;
34572     },
34573     
34574     address_component_from_google_geocode: function(address_components) 
34575     {
34576         var result = {};
34577         
34578         for (var i = 0; i < address_components.length; i++) {
34579             var component = address_components[i];
34580             if (component.types.indexOf("postal_code") >= 0) {
34581                 result.postalCode = component.short_name;
34582             } else if (component.types.indexOf("street_number") >= 0) {
34583                 result.streetNumber = component.short_name;
34584             } else if (component.types.indexOf("route") >= 0) {
34585                 result.streetName = component.short_name;
34586             } else if (component.types.indexOf("neighborhood") >= 0) {
34587                 result.city = component.short_name;
34588             } else if (component.types.indexOf("locality") >= 0) {
34589                 result.city = component.short_name;
34590             } else if (component.types.indexOf("sublocality") >= 0) {
34591                 result.district = component.short_name;
34592             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
34593                 result.stateOrProvince = component.short_name;
34594             } else if (component.types.indexOf("country") >= 0) {
34595                 result.country = component.short_name;
34596             }
34597         }
34598         
34599         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
34600         result.addressLine2 = "";
34601         return result;
34602     },
34603     
34604     setZoomLevel: function(zoom)
34605     {
34606         this.gMapContext.map.setZoom(zoom);
34607     },
34608     
34609     show: function()
34610     {
34611         if(!this.el){
34612             return;
34613         }
34614         
34615         this.el.show();
34616         
34617         this.resize();
34618         
34619         this.fireEvent('show', this);
34620     },
34621     
34622     hide: function()
34623     {
34624         if(!this.el){
34625             return;
34626         }
34627         
34628         this.el.hide();
34629         
34630         this.fireEvent('hide', this);
34631     }
34632     
34633 });
34634
34635 Roo.apply(Roo.bootstrap.LocationPicker, {
34636     
34637     OverlayView : function(map, options)
34638     {
34639         options = options || {};
34640         
34641         this.setMap(map);
34642     }
34643     
34644     
34645 });/**
34646  * @class Roo.bootstrap.Alert
34647  * @extends Roo.bootstrap.Component
34648  * Bootstrap Alert class - shows an alert area box
34649  * eg
34650  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
34651   Enter a valid email address
34652 </div>
34653  * @licence LGPL
34654  * @cfg {String} title The title of alert
34655  * @cfg {String} html The content of alert
34656  * @cfg {String} weight (success|info|warning|danger) Weight of the message
34657  * @cfg {String} fa font-awesomeicon
34658  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
34659  * @cfg {Boolean} close true to show a x closer
34660  * 
34661  * 
34662  * @constructor
34663  * Create a new alert
34664  * @param {Object} config The config object
34665  */
34666
34667
34668 Roo.bootstrap.Alert = function(config){
34669     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
34670     
34671 };
34672
34673 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
34674     
34675     title: '',
34676     html: '',
34677     weight: false,
34678     fa: false,
34679     faicon: false, // BC
34680     close : false,
34681     
34682     
34683     getAutoCreate : function()
34684     {
34685         
34686         var cfg = {
34687             tag : 'div',
34688             cls : 'alert',
34689             cn : [
34690                 {
34691                     tag: 'button',
34692                     type :  "button",
34693                     cls: "close",
34694                     html : '×',
34695                     style : this.close ? '' : 'display:none'
34696                 },
34697                 {
34698                     tag : 'i',
34699                     cls : 'roo-alert-icon'
34700                     
34701                 },
34702                 {
34703                     tag : 'b',
34704                     cls : 'roo-alert-title',
34705                     html : this.title
34706                 },
34707                 {
34708                     tag : 'span',
34709                     cls : 'roo-alert-text',
34710                     html : this.html
34711                 }
34712             ]
34713         };
34714         
34715         if(this.faicon){
34716             cfg.cn[0].cls += ' fa ' + this.faicon;
34717         }
34718         if(this.fa){
34719             cfg.cn[0].cls += ' fa ' + this.fa;
34720         }
34721         
34722         if(this.weight){
34723             cfg.cls += ' alert-' + this.weight;
34724         }
34725         
34726         return cfg;
34727     },
34728     
34729     initEvents: function() 
34730     {
34731         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34732         this.titleEl =  this.el.select('.roo-alert-title',true).first();
34733         this.iconEl = this.el.select('.roo-alert-icon',true).first();
34734         this.htmlEl = this.el.select('.roo-alert-text',true).first();
34735         if (this.seconds > 0) {
34736             this.hide.defer(this.seconds, this);
34737         }
34738     },
34739     /**
34740      * Set the Title Message HTML
34741      * @param {String} html
34742      */
34743     setTitle : function(str)
34744     {
34745         this.titleEl.dom.innerHTML = str;
34746     },
34747      
34748      /**
34749      * Set the Body Message HTML
34750      * @param {String} html
34751      */
34752     setHtml : function(str)
34753     {
34754         this.htmlEl.dom.innerHTML = str;
34755     },
34756     /**
34757      * Set the Weight of the alert
34758      * @param {String} (success|info|warning|danger) weight
34759      */
34760     
34761     setWeight : function(weight)
34762     {
34763         if(this.weight){
34764             this.el.removeClass('alert-' + this.weight);
34765         }
34766         
34767         this.weight = weight;
34768         
34769         this.el.addClass('alert-' + this.weight);
34770     },
34771       /**
34772      * Set the Icon of the alert
34773      * @param {String} see fontawsome names (name without the 'fa-' bit)
34774      */
34775     setIcon : function(icon)
34776     {
34777         if(this.faicon){
34778             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
34779         }
34780         
34781         this.faicon = icon;
34782         
34783         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
34784     },
34785     /**
34786      * Hide the Alert
34787      */
34788     hide: function() 
34789     {
34790         this.el.hide();   
34791     },
34792     /**
34793      * Show the Alert
34794      */
34795     show: function() 
34796     {  
34797         this.el.show();   
34798     }
34799     
34800 });
34801
34802  
34803 /*
34804 * Licence: LGPL
34805 */
34806
34807 /**
34808  * @class Roo.bootstrap.UploadCropbox
34809  * @extends Roo.bootstrap.Component
34810  * Bootstrap UploadCropbox class
34811  * @cfg {String} emptyText show when image has been loaded
34812  * @cfg {String} rotateNotify show when image too small to rotate
34813  * @cfg {Number} errorTimeout default 3000
34814  * @cfg {Number} minWidth default 300
34815  * @cfg {Number} minHeight default 300
34816  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
34817  * @cfg {Boolean} isDocument (true|false) default false
34818  * @cfg {String} url action url
34819  * @cfg {String} paramName default 'imageUpload'
34820  * @cfg {String} method default POST
34821  * @cfg {Boolean} loadMask (true|false) default true
34822  * @cfg {Boolean} loadingText default 'Loading...'
34823  * 
34824  * @constructor
34825  * Create a new UploadCropbox
34826  * @param {Object} config The config object
34827  */
34828
34829 Roo.bootstrap.UploadCropbox = function(config){
34830     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
34831     
34832     this.addEvents({
34833         /**
34834          * @event beforeselectfile
34835          * Fire before select file
34836          * @param {Roo.bootstrap.UploadCropbox} this
34837          */
34838         "beforeselectfile" : true,
34839         /**
34840          * @event initial
34841          * Fire after initEvent
34842          * @param {Roo.bootstrap.UploadCropbox} this
34843          */
34844         "initial" : true,
34845         /**
34846          * @event crop
34847          * Fire after initEvent
34848          * @param {Roo.bootstrap.UploadCropbox} this
34849          * @param {String} data
34850          */
34851         "crop" : true,
34852         /**
34853          * @event prepare
34854          * Fire when preparing the file data
34855          * @param {Roo.bootstrap.UploadCropbox} this
34856          * @param {Object} file
34857          */
34858         "prepare" : true,
34859         /**
34860          * @event exception
34861          * Fire when get exception
34862          * @param {Roo.bootstrap.UploadCropbox} this
34863          * @param {XMLHttpRequest} xhr
34864          */
34865         "exception" : true,
34866         /**
34867          * @event beforeloadcanvas
34868          * Fire before load the canvas
34869          * @param {Roo.bootstrap.UploadCropbox} this
34870          * @param {String} src
34871          */
34872         "beforeloadcanvas" : true,
34873         /**
34874          * @event trash
34875          * Fire when trash image
34876          * @param {Roo.bootstrap.UploadCropbox} this
34877          */
34878         "trash" : true,
34879         /**
34880          * @event download
34881          * Fire when download the image
34882          * @param {Roo.bootstrap.UploadCropbox} this
34883          */
34884         "download" : true,
34885         /**
34886          * @event footerbuttonclick
34887          * Fire when footerbuttonclick
34888          * @param {Roo.bootstrap.UploadCropbox} this
34889          * @param {String} type
34890          */
34891         "footerbuttonclick" : true,
34892         /**
34893          * @event resize
34894          * Fire when resize
34895          * @param {Roo.bootstrap.UploadCropbox} this
34896          */
34897         "resize" : true,
34898         /**
34899          * @event rotate
34900          * Fire when rotate the image
34901          * @param {Roo.bootstrap.UploadCropbox} this
34902          * @param {String} pos
34903          */
34904         "rotate" : true,
34905         /**
34906          * @event inspect
34907          * Fire when inspect the file
34908          * @param {Roo.bootstrap.UploadCropbox} this
34909          * @param {Object} file
34910          */
34911         "inspect" : true,
34912         /**
34913          * @event upload
34914          * Fire when xhr upload the file
34915          * @param {Roo.bootstrap.UploadCropbox} this
34916          * @param {Object} data
34917          */
34918         "upload" : true,
34919         /**
34920          * @event arrange
34921          * Fire when arrange the file data
34922          * @param {Roo.bootstrap.UploadCropbox} this
34923          * @param {Object} formData
34924          */
34925         "arrange" : true
34926     });
34927     
34928     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
34929 };
34930
34931 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
34932     
34933     emptyText : 'Click to upload image',
34934     rotateNotify : 'Image is too small to rotate',
34935     errorTimeout : 3000,
34936     scale : 0,
34937     baseScale : 1,
34938     rotate : 0,
34939     dragable : false,
34940     pinching : false,
34941     mouseX : 0,
34942     mouseY : 0,
34943     cropData : false,
34944     minWidth : 300,
34945     minHeight : 300,
34946     file : false,
34947     exif : {},
34948     baseRotate : 1,
34949     cropType : 'image/jpeg',
34950     buttons : false,
34951     canvasLoaded : false,
34952     isDocument : false,
34953     method : 'POST',
34954     paramName : 'imageUpload',
34955     loadMask : true,
34956     loadingText : 'Loading...',
34957     maskEl : false,
34958     
34959     getAutoCreate : function()
34960     {
34961         var cfg = {
34962             tag : 'div',
34963             cls : 'roo-upload-cropbox',
34964             cn : [
34965                 {
34966                     tag : 'input',
34967                     cls : 'roo-upload-cropbox-selector',
34968                     type : 'file'
34969                 },
34970                 {
34971                     tag : 'div',
34972                     cls : 'roo-upload-cropbox-body',
34973                     style : 'cursor:pointer',
34974                     cn : [
34975                         {
34976                             tag : 'div',
34977                             cls : 'roo-upload-cropbox-preview'
34978                         },
34979                         {
34980                             tag : 'div',
34981                             cls : 'roo-upload-cropbox-thumb'
34982                         },
34983                         {
34984                             tag : 'div',
34985                             cls : 'roo-upload-cropbox-empty-notify',
34986                             html : this.emptyText
34987                         },
34988                         {
34989                             tag : 'div',
34990                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
34991                             html : this.rotateNotify
34992                         }
34993                     ]
34994                 },
34995                 {
34996                     tag : 'div',
34997                     cls : 'roo-upload-cropbox-footer',
34998                     cn : {
34999                         tag : 'div',
35000                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35001                         cn : []
35002                     }
35003                 }
35004             ]
35005         };
35006         
35007         return cfg;
35008     },
35009     
35010     onRender : function(ct, position)
35011     {
35012         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35013         
35014         if (this.buttons.length) {
35015             
35016             Roo.each(this.buttons, function(bb) {
35017                 
35018                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35019                 
35020                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35021                 
35022             }, this);
35023         }
35024         
35025         if(this.loadMask){
35026             this.maskEl = this.el;
35027         }
35028     },
35029     
35030     initEvents : function()
35031     {
35032         this.urlAPI = (window.createObjectURL && window) || 
35033                                 (window.URL && URL.revokeObjectURL && URL) || 
35034                                 (window.webkitURL && webkitURL);
35035                         
35036         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35037         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35038         
35039         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35040         this.selectorEl.hide();
35041         
35042         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35043         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35044         
35045         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35046         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35047         this.thumbEl.hide();
35048         
35049         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35050         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35051         
35052         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35053         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35054         this.errorEl.hide();
35055         
35056         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35057         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35058         this.footerEl.hide();
35059         
35060         this.setThumbBoxSize();
35061         
35062         this.bind();
35063         
35064         this.resize();
35065         
35066         this.fireEvent('initial', this);
35067     },
35068
35069     bind : function()
35070     {
35071         var _this = this;
35072         
35073         window.addEventListener("resize", function() { _this.resize(); } );
35074         
35075         this.bodyEl.on('click', this.beforeSelectFile, this);
35076         
35077         if(Roo.isTouch){
35078             this.bodyEl.on('touchstart', this.onTouchStart, this);
35079             this.bodyEl.on('touchmove', this.onTouchMove, this);
35080             this.bodyEl.on('touchend', this.onTouchEnd, this);
35081         }
35082         
35083         if(!Roo.isTouch){
35084             this.bodyEl.on('mousedown', this.onMouseDown, this);
35085             this.bodyEl.on('mousemove', this.onMouseMove, this);
35086             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35087             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35088             Roo.get(document).on('mouseup', this.onMouseUp, this);
35089         }
35090         
35091         this.selectorEl.on('change', this.onFileSelected, this);
35092     },
35093     
35094     reset : function()
35095     {    
35096         this.scale = 0;
35097         this.baseScale = 1;
35098         this.rotate = 0;
35099         this.baseRotate = 1;
35100         this.dragable = false;
35101         this.pinching = false;
35102         this.mouseX = 0;
35103         this.mouseY = 0;
35104         this.cropData = false;
35105         this.notifyEl.dom.innerHTML = this.emptyText;
35106         
35107         this.selectorEl.dom.value = '';
35108         
35109     },
35110     
35111     resize : function()
35112     {
35113         if(this.fireEvent('resize', this) != false){
35114             this.setThumbBoxPosition();
35115             this.setCanvasPosition();
35116         }
35117     },
35118     
35119     onFooterButtonClick : function(e, el, o, type)
35120     {
35121         switch (type) {
35122             case 'rotate-left' :
35123                 this.onRotateLeft(e);
35124                 break;
35125             case 'rotate-right' :
35126                 this.onRotateRight(e);
35127                 break;
35128             case 'picture' :
35129                 this.beforeSelectFile(e);
35130                 break;
35131             case 'trash' :
35132                 this.trash(e);
35133                 break;
35134             case 'crop' :
35135                 this.crop(e);
35136                 break;
35137             case 'download' :
35138                 this.download(e);
35139                 break;
35140             default :
35141                 break;
35142         }
35143         
35144         this.fireEvent('footerbuttonclick', this, type);
35145     },
35146     
35147     beforeSelectFile : function(e)
35148     {
35149         e.preventDefault();
35150         
35151         if(this.fireEvent('beforeselectfile', this) != false){
35152             this.selectorEl.dom.click();
35153         }
35154     },
35155     
35156     onFileSelected : function(e)
35157     {
35158         e.preventDefault();
35159         
35160         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35161             return;
35162         }
35163         
35164         var file = this.selectorEl.dom.files[0];
35165         
35166         if(this.fireEvent('inspect', this, file) != false){
35167             this.prepare(file);
35168         }
35169         
35170     },
35171     
35172     trash : function(e)
35173     {
35174         this.fireEvent('trash', this);
35175     },
35176     
35177     download : function(e)
35178     {
35179         this.fireEvent('download', this);
35180     },
35181     
35182     loadCanvas : function(src)
35183     {   
35184         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35185             
35186             this.reset();
35187             
35188             this.imageEl = document.createElement('img');
35189             
35190             var _this = this;
35191             
35192             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35193             
35194             this.imageEl.src = src;
35195         }
35196     },
35197     
35198     onLoadCanvas : function()
35199     {   
35200         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35201         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35202         
35203         this.bodyEl.un('click', this.beforeSelectFile, this);
35204         
35205         this.notifyEl.hide();
35206         this.thumbEl.show();
35207         this.footerEl.show();
35208         
35209         this.baseRotateLevel();
35210         
35211         if(this.isDocument){
35212             this.setThumbBoxSize();
35213         }
35214         
35215         this.setThumbBoxPosition();
35216         
35217         this.baseScaleLevel();
35218         
35219         this.draw();
35220         
35221         this.resize();
35222         
35223         this.canvasLoaded = true;
35224         
35225         if(this.loadMask){
35226             this.maskEl.unmask();
35227         }
35228         
35229     },
35230     
35231     setCanvasPosition : function()
35232     {   
35233         if(!this.canvasEl){
35234             return;
35235         }
35236         
35237         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35238         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35239         
35240         this.previewEl.setLeft(pw);
35241         this.previewEl.setTop(ph);
35242         
35243     },
35244     
35245     onMouseDown : function(e)
35246     {   
35247         e.stopEvent();
35248         
35249         this.dragable = true;
35250         this.pinching = false;
35251         
35252         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35253             this.dragable = false;
35254             return;
35255         }
35256         
35257         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35258         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35259         
35260     },
35261     
35262     onMouseMove : function(e)
35263     {   
35264         e.stopEvent();
35265         
35266         if(!this.canvasLoaded){
35267             return;
35268         }
35269         
35270         if (!this.dragable){
35271             return;
35272         }
35273         
35274         var minX = Math.ceil(this.thumbEl.getLeft(true));
35275         var minY = Math.ceil(this.thumbEl.getTop(true));
35276         
35277         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35278         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35279         
35280         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35281         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35282         
35283         x = x - this.mouseX;
35284         y = y - this.mouseY;
35285         
35286         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35287         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35288         
35289         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35290         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35291         
35292         this.previewEl.setLeft(bgX);
35293         this.previewEl.setTop(bgY);
35294         
35295         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35296         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35297     },
35298     
35299     onMouseUp : function(e)
35300     {   
35301         e.stopEvent();
35302         
35303         this.dragable = false;
35304     },
35305     
35306     onMouseWheel : function(e)
35307     {   
35308         e.stopEvent();
35309         
35310         this.startScale = this.scale;
35311         
35312         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35313         
35314         if(!this.zoomable()){
35315             this.scale = this.startScale;
35316             return;
35317         }
35318         
35319         this.draw();
35320         
35321         return;
35322     },
35323     
35324     zoomable : function()
35325     {
35326         var minScale = this.thumbEl.getWidth() / this.minWidth;
35327         
35328         if(this.minWidth < this.minHeight){
35329             minScale = this.thumbEl.getHeight() / this.minHeight;
35330         }
35331         
35332         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35333         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35334         
35335         if(
35336                 this.isDocument &&
35337                 (this.rotate == 0 || this.rotate == 180) && 
35338                 (
35339                     width > this.imageEl.OriginWidth || 
35340                     height > this.imageEl.OriginHeight ||
35341                     (width < this.minWidth && height < this.minHeight)
35342                 )
35343         ){
35344             return false;
35345         }
35346         
35347         if(
35348                 this.isDocument &&
35349                 (this.rotate == 90 || this.rotate == 270) && 
35350                 (
35351                     width > this.imageEl.OriginWidth || 
35352                     height > this.imageEl.OriginHeight ||
35353                     (width < this.minHeight && height < this.minWidth)
35354                 )
35355         ){
35356             return false;
35357         }
35358         
35359         if(
35360                 !this.isDocument &&
35361                 (this.rotate == 0 || this.rotate == 180) && 
35362                 (
35363                     width < this.minWidth || 
35364                     width > this.imageEl.OriginWidth || 
35365                     height < this.minHeight || 
35366                     height > this.imageEl.OriginHeight
35367                 )
35368         ){
35369             return false;
35370         }
35371         
35372         if(
35373                 !this.isDocument &&
35374                 (this.rotate == 90 || this.rotate == 270) && 
35375                 (
35376                     width < this.minHeight || 
35377                     width > this.imageEl.OriginWidth || 
35378                     height < this.minWidth || 
35379                     height > this.imageEl.OriginHeight
35380                 )
35381         ){
35382             return false;
35383         }
35384         
35385         return true;
35386         
35387     },
35388     
35389     onRotateLeft : function(e)
35390     {   
35391         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35392             
35393             var minScale = this.thumbEl.getWidth() / this.minWidth;
35394             
35395             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35396             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35397             
35398             this.startScale = this.scale;
35399             
35400             while (this.getScaleLevel() < minScale){
35401             
35402                 this.scale = this.scale + 1;
35403                 
35404                 if(!this.zoomable()){
35405                     break;
35406                 }
35407                 
35408                 if(
35409                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35410                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35411                 ){
35412                     continue;
35413                 }
35414                 
35415                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35416
35417                 this.draw();
35418                 
35419                 return;
35420             }
35421             
35422             this.scale = this.startScale;
35423             
35424             this.onRotateFail();
35425             
35426             return false;
35427         }
35428         
35429         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35430
35431         if(this.isDocument){
35432             this.setThumbBoxSize();
35433             this.setThumbBoxPosition();
35434             this.setCanvasPosition();
35435         }
35436         
35437         this.draw();
35438         
35439         this.fireEvent('rotate', this, 'left');
35440         
35441     },
35442     
35443     onRotateRight : function(e)
35444     {
35445         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35446             
35447             var minScale = this.thumbEl.getWidth() / this.minWidth;
35448         
35449             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35450             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35451             
35452             this.startScale = this.scale;
35453             
35454             while (this.getScaleLevel() < minScale){
35455             
35456                 this.scale = this.scale + 1;
35457                 
35458                 if(!this.zoomable()){
35459                     break;
35460                 }
35461                 
35462                 if(
35463                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35464                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35465                 ){
35466                     continue;
35467                 }
35468                 
35469                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35470
35471                 this.draw();
35472                 
35473                 return;
35474             }
35475             
35476             this.scale = this.startScale;
35477             
35478             this.onRotateFail();
35479             
35480             return false;
35481         }
35482         
35483         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35484
35485         if(this.isDocument){
35486             this.setThumbBoxSize();
35487             this.setThumbBoxPosition();
35488             this.setCanvasPosition();
35489         }
35490         
35491         this.draw();
35492         
35493         this.fireEvent('rotate', this, 'right');
35494     },
35495     
35496     onRotateFail : function()
35497     {
35498         this.errorEl.show(true);
35499         
35500         var _this = this;
35501         
35502         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35503     },
35504     
35505     draw : function()
35506     {
35507         this.previewEl.dom.innerHTML = '';
35508         
35509         var canvasEl = document.createElement("canvas");
35510         
35511         var contextEl = canvasEl.getContext("2d");
35512         
35513         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35514         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35515         var center = this.imageEl.OriginWidth / 2;
35516         
35517         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35518             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35519             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35520             center = this.imageEl.OriginHeight / 2;
35521         }
35522         
35523         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35524         
35525         contextEl.translate(center, center);
35526         contextEl.rotate(this.rotate * Math.PI / 180);
35527
35528         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35529         
35530         this.canvasEl = document.createElement("canvas");
35531         
35532         this.contextEl = this.canvasEl.getContext("2d");
35533         
35534         switch (this.rotate) {
35535             case 0 :
35536                 
35537                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35538                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35539                 
35540                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35541                 
35542                 break;
35543             case 90 : 
35544                 
35545                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35546                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35547                 
35548                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35549                     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);
35550                     break;
35551                 }
35552                 
35553                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35554                 
35555                 break;
35556             case 180 :
35557                 
35558                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35559                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35560                 
35561                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35562                     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);
35563                     break;
35564                 }
35565                 
35566                 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);
35567                 
35568                 break;
35569             case 270 :
35570                 
35571                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35572                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35573         
35574                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35575                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35576                     break;
35577                 }
35578                 
35579                 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);
35580                 
35581                 break;
35582             default : 
35583                 break;
35584         }
35585         
35586         this.previewEl.appendChild(this.canvasEl);
35587         
35588         this.setCanvasPosition();
35589     },
35590     
35591     crop : function()
35592     {
35593         if(!this.canvasLoaded){
35594             return;
35595         }
35596         
35597         var imageCanvas = document.createElement("canvas");
35598         
35599         var imageContext = imageCanvas.getContext("2d");
35600         
35601         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35602         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35603         
35604         var center = imageCanvas.width / 2;
35605         
35606         imageContext.translate(center, center);
35607         
35608         imageContext.rotate(this.rotate * Math.PI / 180);
35609         
35610         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35611         
35612         var canvas = document.createElement("canvas");
35613         
35614         var context = canvas.getContext("2d");
35615                 
35616         canvas.width = this.minWidth;
35617         canvas.height = this.minHeight;
35618
35619         switch (this.rotate) {
35620             case 0 :
35621                 
35622                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35623                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35624                 
35625                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35626                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35627                 
35628                 var targetWidth = this.minWidth - 2 * x;
35629                 var targetHeight = this.minHeight - 2 * y;
35630                 
35631                 var scale = 1;
35632                 
35633                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35634                     scale = targetWidth / width;
35635                 }
35636                 
35637                 if(x > 0 && y == 0){
35638                     scale = targetHeight / height;
35639                 }
35640                 
35641                 if(x > 0 && y > 0){
35642                     scale = targetWidth / width;
35643                     
35644                     if(width < height){
35645                         scale = targetHeight / height;
35646                     }
35647                 }
35648                 
35649                 context.scale(scale, scale);
35650                 
35651                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35652                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35653
35654                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35655                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35656
35657                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35658                 
35659                 break;
35660             case 90 : 
35661                 
35662                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35663                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35664                 
35665                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35666                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35667                 
35668                 var targetWidth = this.minWidth - 2 * x;
35669                 var targetHeight = this.minHeight - 2 * y;
35670                 
35671                 var scale = 1;
35672                 
35673                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35674                     scale = targetWidth / width;
35675                 }
35676                 
35677                 if(x > 0 && y == 0){
35678                     scale = targetHeight / height;
35679                 }
35680                 
35681                 if(x > 0 && y > 0){
35682                     scale = targetWidth / width;
35683                     
35684                     if(width < height){
35685                         scale = targetHeight / height;
35686                     }
35687                 }
35688                 
35689                 context.scale(scale, scale);
35690                 
35691                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35692                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35693
35694                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35695                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35696                 
35697                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35698                 
35699                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35700                 
35701                 break;
35702             case 180 :
35703                 
35704                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35705                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35706                 
35707                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35708                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35709                 
35710                 var targetWidth = this.minWidth - 2 * x;
35711                 var targetHeight = this.minHeight - 2 * y;
35712                 
35713                 var scale = 1;
35714                 
35715                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35716                     scale = targetWidth / width;
35717                 }
35718                 
35719                 if(x > 0 && y == 0){
35720                     scale = targetHeight / height;
35721                 }
35722                 
35723                 if(x > 0 && y > 0){
35724                     scale = targetWidth / width;
35725                     
35726                     if(width < height){
35727                         scale = targetHeight / height;
35728                     }
35729                 }
35730                 
35731                 context.scale(scale, scale);
35732                 
35733                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35734                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35735
35736                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35737                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35738
35739                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35740                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35741                 
35742                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35743                 
35744                 break;
35745             case 270 :
35746                 
35747                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35748                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35749                 
35750                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35751                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35752                 
35753                 var targetWidth = this.minWidth - 2 * x;
35754                 var targetHeight = this.minHeight - 2 * y;
35755                 
35756                 var scale = 1;
35757                 
35758                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35759                     scale = targetWidth / width;
35760                 }
35761                 
35762                 if(x > 0 && y == 0){
35763                     scale = targetHeight / height;
35764                 }
35765                 
35766                 if(x > 0 && y > 0){
35767                     scale = targetWidth / width;
35768                     
35769                     if(width < height){
35770                         scale = targetHeight / height;
35771                     }
35772                 }
35773                 
35774                 context.scale(scale, scale);
35775                 
35776                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35777                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35778
35779                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35780                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35781                 
35782                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35783                 
35784                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35785                 
35786                 break;
35787             default : 
35788                 break;
35789         }
35790         
35791         this.cropData = canvas.toDataURL(this.cropType);
35792         
35793         if(this.fireEvent('crop', this, this.cropData) !== false){
35794             this.process(this.file, this.cropData);
35795         }
35796         
35797         return;
35798         
35799     },
35800     
35801     setThumbBoxSize : function()
35802     {
35803         var width, height;
35804         
35805         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
35806             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
35807             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
35808             
35809             this.minWidth = width;
35810             this.minHeight = height;
35811             
35812             if(this.rotate == 90 || this.rotate == 270){
35813                 this.minWidth = height;
35814                 this.minHeight = width;
35815             }
35816         }
35817         
35818         height = 300;
35819         width = Math.ceil(this.minWidth * height / this.minHeight);
35820         
35821         if(this.minWidth > this.minHeight){
35822             width = 300;
35823             height = Math.ceil(this.minHeight * width / this.minWidth);
35824         }
35825         
35826         this.thumbEl.setStyle({
35827             width : width + 'px',
35828             height : height + 'px'
35829         });
35830
35831         return;
35832             
35833     },
35834     
35835     setThumbBoxPosition : function()
35836     {
35837         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
35838         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
35839         
35840         this.thumbEl.setLeft(x);
35841         this.thumbEl.setTop(y);
35842         
35843     },
35844     
35845     baseRotateLevel : function()
35846     {
35847         this.baseRotate = 1;
35848         
35849         if(
35850                 typeof(this.exif) != 'undefined' &&
35851                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
35852                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
35853         ){
35854             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
35855         }
35856         
35857         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
35858         
35859     },
35860     
35861     baseScaleLevel : function()
35862     {
35863         var width, height;
35864         
35865         if(this.isDocument){
35866             
35867             if(this.baseRotate == 6 || this.baseRotate == 8){
35868             
35869                 height = this.thumbEl.getHeight();
35870                 this.baseScale = height / this.imageEl.OriginWidth;
35871
35872                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
35873                     width = this.thumbEl.getWidth();
35874                     this.baseScale = width / this.imageEl.OriginHeight;
35875                 }
35876
35877                 return;
35878             }
35879
35880             height = this.thumbEl.getHeight();
35881             this.baseScale = height / this.imageEl.OriginHeight;
35882
35883             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
35884                 width = this.thumbEl.getWidth();
35885                 this.baseScale = width / this.imageEl.OriginWidth;
35886             }
35887
35888             return;
35889         }
35890         
35891         if(this.baseRotate == 6 || this.baseRotate == 8){
35892             
35893             width = this.thumbEl.getHeight();
35894             this.baseScale = width / this.imageEl.OriginHeight;
35895             
35896             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
35897                 height = this.thumbEl.getWidth();
35898                 this.baseScale = height / this.imageEl.OriginHeight;
35899             }
35900             
35901             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35902                 height = this.thumbEl.getWidth();
35903                 this.baseScale = height / this.imageEl.OriginHeight;
35904                 
35905                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
35906                     width = this.thumbEl.getHeight();
35907                     this.baseScale = width / this.imageEl.OriginWidth;
35908                 }
35909             }
35910             
35911             return;
35912         }
35913         
35914         width = this.thumbEl.getWidth();
35915         this.baseScale = width / this.imageEl.OriginWidth;
35916         
35917         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
35918             height = this.thumbEl.getHeight();
35919             this.baseScale = height / this.imageEl.OriginHeight;
35920         }
35921         
35922         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35923             
35924             height = this.thumbEl.getHeight();
35925             this.baseScale = height / this.imageEl.OriginHeight;
35926             
35927             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
35928                 width = this.thumbEl.getWidth();
35929                 this.baseScale = width / this.imageEl.OriginWidth;
35930             }
35931             
35932         }
35933         
35934         return;
35935     },
35936     
35937     getScaleLevel : function()
35938     {
35939         return this.baseScale * Math.pow(1.1, this.scale);
35940     },
35941     
35942     onTouchStart : function(e)
35943     {
35944         if(!this.canvasLoaded){
35945             this.beforeSelectFile(e);
35946             return;
35947         }
35948         
35949         var touches = e.browserEvent.touches;
35950         
35951         if(!touches){
35952             return;
35953         }
35954         
35955         if(touches.length == 1){
35956             this.onMouseDown(e);
35957             return;
35958         }
35959         
35960         if(touches.length != 2){
35961             return;
35962         }
35963         
35964         var coords = [];
35965         
35966         for(var i = 0, finger; finger = touches[i]; i++){
35967             coords.push(finger.pageX, finger.pageY);
35968         }
35969         
35970         var x = Math.pow(coords[0] - coords[2], 2);
35971         var y = Math.pow(coords[1] - coords[3], 2);
35972         
35973         this.startDistance = Math.sqrt(x + y);
35974         
35975         this.startScale = this.scale;
35976         
35977         this.pinching = true;
35978         this.dragable = false;
35979         
35980     },
35981     
35982     onTouchMove : function(e)
35983     {
35984         if(!this.pinching && !this.dragable){
35985             return;
35986         }
35987         
35988         var touches = e.browserEvent.touches;
35989         
35990         if(!touches){
35991             return;
35992         }
35993         
35994         if(this.dragable){
35995             this.onMouseMove(e);
35996             return;
35997         }
35998         
35999         var coords = [];
36000         
36001         for(var i = 0, finger; finger = touches[i]; i++){
36002             coords.push(finger.pageX, finger.pageY);
36003         }
36004         
36005         var x = Math.pow(coords[0] - coords[2], 2);
36006         var y = Math.pow(coords[1] - coords[3], 2);
36007         
36008         this.endDistance = Math.sqrt(x + y);
36009         
36010         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36011         
36012         if(!this.zoomable()){
36013             this.scale = this.startScale;
36014             return;
36015         }
36016         
36017         this.draw();
36018         
36019     },
36020     
36021     onTouchEnd : function(e)
36022     {
36023         this.pinching = false;
36024         this.dragable = false;
36025         
36026     },
36027     
36028     process : function(file, crop)
36029     {
36030         if(this.loadMask){
36031             this.maskEl.mask(this.loadingText);
36032         }
36033         
36034         this.xhr = new XMLHttpRequest();
36035         
36036         file.xhr = this.xhr;
36037
36038         this.xhr.open(this.method, this.url, true);
36039         
36040         var headers = {
36041             "Accept": "application/json",
36042             "Cache-Control": "no-cache",
36043             "X-Requested-With": "XMLHttpRequest"
36044         };
36045         
36046         for (var headerName in headers) {
36047             var headerValue = headers[headerName];
36048             if (headerValue) {
36049                 this.xhr.setRequestHeader(headerName, headerValue);
36050             }
36051         }
36052         
36053         var _this = this;
36054         
36055         this.xhr.onload = function()
36056         {
36057             _this.xhrOnLoad(_this.xhr);
36058         }
36059         
36060         this.xhr.onerror = function()
36061         {
36062             _this.xhrOnError(_this.xhr);
36063         }
36064         
36065         var formData = new FormData();
36066
36067         formData.append('returnHTML', 'NO');
36068         
36069         if(crop){
36070             formData.append('crop', crop);
36071         }
36072         
36073         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36074             formData.append(this.paramName, file, file.name);
36075         }
36076         
36077         if(typeof(file.filename) != 'undefined'){
36078             formData.append('filename', file.filename);
36079         }
36080         
36081         if(typeof(file.mimetype) != 'undefined'){
36082             formData.append('mimetype', file.mimetype);
36083         }
36084         
36085         if(this.fireEvent('arrange', this, formData) != false){
36086             this.xhr.send(formData);
36087         };
36088     },
36089     
36090     xhrOnLoad : function(xhr)
36091     {
36092         if(this.loadMask){
36093             this.maskEl.unmask();
36094         }
36095         
36096         if (xhr.readyState !== 4) {
36097             this.fireEvent('exception', this, xhr);
36098             return;
36099         }
36100
36101         var response = Roo.decode(xhr.responseText);
36102         
36103         if(!response.success){
36104             this.fireEvent('exception', this, xhr);
36105             return;
36106         }
36107         
36108         var response = Roo.decode(xhr.responseText);
36109         
36110         this.fireEvent('upload', this, response);
36111         
36112     },
36113     
36114     xhrOnError : function()
36115     {
36116         if(this.loadMask){
36117             this.maskEl.unmask();
36118         }
36119         
36120         Roo.log('xhr on error');
36121         
36122         var response = Roo.decode(xhr.responseText);
36123           
36124         Roo.log(response);
36125         
36126     },
36127     
36128     prepare : function(file)
36129     {   
36130         if(this.loadMask){
36131             this.maskEl.mask(this.loadingText);
36132         }
36133         
36134         this.file = false;
36135         this.exif = {};
36136         
36137         if(typeof(file) === 'string'){
36138             this.loadCanvas(file);
36139             return;
36140         }
36141         
36142         if(!file || !this.urlAPI){
36143             return;
36144         }
36145         
36146         this.file = file;
36147         this.cropType = file.type;
36148         
36149         var _this = this;
36150         
36151         if(this.fireEvent('prepare', this, this.file) != false){
36152             
36153             var reader = new FileReader();
36154             
36155             reader.onload = function (e) {
36156                 if (e.target.error) {
36157                     Roo.log(e.target.error);
36158                     return;
36159                 }
36160                 
36161                 var buffer = e.target.result,
36162                     dataView = new DataView(buffer),
36163                     offset = 2,
36164                     maxOffset = dataView.byteLength - 4,
36165                     markerBytes,
36166                     markerLength;
36167                 
36168                 if (dataView.getUint16(0) === 0xffd8) {
36169                     while (offset < maxOffset) {
36170                         markerBytes = dataView.getUint16(offset);
36171                         
36172                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36173                             markerLength = dataView.getUint16(offset + 2) + 2;
36174                             if (offset + markerLength > dataView.byteLength) {
36175                                 Roo.log('Invalid meta data: Invalid segment size.');
36176                                 break;
36177                             }
36178                             
36179                             if(markerBytes == 0xffe1){
36180                                 _this.parseExifData(
36181                                     dataView,
36182                                     offset,
36183                                     markerLength
36184                                 );
36185                             }
36186                             
36187                             offset += markerLength;
36188                             
36189                             continue;
36190                         }
36191                         
36192                         break;
36193                     }
36194                     
36195                 }
36196                 
36197                 var url = _this.urlAPI.createObjectURL(_this.file);
36198                 
36199                 _this.loadCanvas(url);
36200                 
36201                 return;
36202             }
36203             
36204             reader.readAsArrayBuffer(this.file);
36205             
36206         }
36207         
36208     },
36209     
36210     parseExifData : function(dataView, offset, length)
36211     {
36212         var tiffOffset = offset + 10,
36213             littleEndian,
36214             dirOffset;
36215     
36216         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36217             // No Exif data, might be XMP data instead
36218             return;
36219         }
36220         
36221         // Check for the ASCII code for "Exif" (0x45786966):
36222         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36223             // No Exif data, might be XMP data instead
36224             return;
36225         }
36226         if (tiffOffset + 8 > dataView.byteLength) {
36227             Roo.log('Invalid Exif data: Invalid segment size.');
36228             return;
36229         }
36230         // Check for the two null bytes:
36231         if (dataView.getUint16(offset + 8) !== 0x0000) {
36232             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36233             return;
36234         }
36235         // Check the byte alignment:
36236         switch (dataView.getUint16(tiffOffset)) {
36237         case 0x4949:
36238             littleEndian = true;
36239             break;
36240         case 0x4D4D:
36241             littleEndian = false;
36242             break;
36243         default:
36244             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36245             return;
36246         }
36247         // Check for the TIFF tag marker (0x002A):
36248         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36249             Roo.log('Invalid Exif data: Missing TIFF marker.');
36250             return;
36251         }
36252         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36253         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36254         
36255         this.parseExifTags(
36256             dataView,
36257             tiffOffset,
36258             tiffOffset + dirOffset,
36259             littleEndian
36260         );
36261     },
36262     
36263     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36264     {
36265         var tagsNumber,
36266             dirEndOffset,
36267             i;
36268         if (dirOffset + 6 > dataView.byteLength) {
36269             Roo.log('Invalid Exif data: Invalid directory offset.');
36270             return;
36271         }
36272         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36273         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36274         if (dirEndOffset + 4 > dataView.byteLength) {
36275             Roo.log('Invalid Exif data: Invalid directory size.');
36276             return;
36277         }
36278         for (i = 0; i < tagsNumber; i += 1) {
36279             this.parseExifTag(
36280                 dataView,
36281                 tiffOffset,
36282                 dirOffset + 2 + 12 * i, // tag offset
36283                 littleEndian
36284             );
36285         }
36286         // Return the offset to the next directory:
36287         return dataView.getUint32(dirEndOffset, littleEndian);
36288     },
36289     
36290     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36291     {
36292         var tag = dataView.getUint16(offset, littleEndian);
36293         
36294         this.exif[tag] = this.getExifValue(
36295             dataView,
36296             tiffOffset,
36297             offset,
36298             dataView.getUint16(offset + 2, littleEndian), // tag type
36299             dataView.getUint32(offset + 4, littleEndian), // tag length
36300             littleEndian
36301         );
36302     },
36303     
36304     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36305     {
36306         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36307             tagSize,
36308             dataOffset,
36309             values,
36310             i,
36311             str,
36312             c;
36313     
36314         if (!tagType) {
36315             Roo.log('Invalid Exif data: Invalid tag type.');
36316             return;
36317         }
36318         
36319         tagSize = tagType.size * length;
36320         // Determine if the value is contained in the dataOffset bytes,
36321         // or if the value at the dataOffset is a pointer to the actual data:
36322         dataOffset = tagSize > 4 ?
36323                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36324         if (dataOffset + tagSize > dataView.byteLength) {
36325             Roo.log('Invalid Exif data: Invalid data offset.');
36326             return;
36327         }
36328         if (length === 1) {
36329             return tagType.getValue(dataView, dataOffset, littleEndian);
36330         }
36331         values = [];
36332         for (i = 0; i < length; i += 1) {
36333             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36334         }
36335         
36336         if (tagType.ascii) {
36337             str = '';
36338             // Concatenate the chars:
36339             for (i = 0; i < values.length; i += 1) {
36340                 c = values[i];
36341                 // Ignore the terminating NULL byte(s):
36342                 if (c === '\u0000') {
36343                     break;
36344                 }
36345                 str += c;
36346             }
36347             return str;
36348         }
36349         return values;
36350     }
36351     
36352 });
36353
36354 Roo.apply(Roo.bootstrap.UploadCropbox, {
36355     tags : {
36356         'Orientation': 0x0112
36357     },
36358     
36359     Orientation: {
36360             1: 0, //'top-left',
36361 //            2: 'top-right',
36362             3: 180, //'bottom-right',
36363 //            4: 'bottom-left',
36364 //            5: 'left-top',
36365             6: 90, //'right-top',
36366 //            7: 'right-bottom',
36367             8: 270 //'left-bottom'
36368     },
36369     
36370     exifTagTypes : {
36371         // byte, 8-bit unsigned int:
36372         1: {
36373             getValue: function (dataView, dataOffset) {
36374                 return dataView.getUint8(dataOffset);
36375             },
36376             size: 1
36377         },
36378         // ascii, 8-bit byte:
36379         2: {
36380             getValue: function (dataView, dataOffset) {
36381                 return String.fromCharCode(dataView.getUint8(dataOffset));
36382             },
36383             size: 1,
36384             ascii: true
36385         },
36386         // short, 16 bit int:
36387         3: {
36388             getValue: function (dataView, dataOffset, littleEndian) {
36389                 return dataView.getUint16(dataOffset, littleEndian);
36390             },
36391             size: 2
36392         },
36393         // long, 32 bit int:
36394         4: {
36395             getValue: function (dataView, dataOffset, littleEndian) {
36396                 return dataView.getUint32(dataOffset, littleEndian);
36397             },
36398             size: 4
36399         },
36400         // rational = two long values, first is numerator, second is denominator:
36401         5: {
36402             getValue: function (dataView, dataOffset, littleEndian) {
36403                 return dataView.getUint32(dataOffset, littleEndian) /
36404                     dataView.getUint32(dataOffset + 4, littleEndian);
36405             },
36406             size: 8
36407         },
36408         // slong, 32 bit signed int:
36409         9: {
36410             getValue: function (dataView, dataOffset, littleEndian) {
36411                 return dataView.getInt32(dataOffset, littleEndian);
36412             },
36413             size: 4
36414         },
36415         // srational, two slongs, first is numerator, second is denominator:
36416         10: {
36417             getValue: function (dataView, dataOffset, littleEndian) {
36418                 return dataView.getInt32(dataOffset, littleEndian) /
36419                     dataView.getInt32(dataOffset + 4, littleEndian);
36420             },
36421             size: 8
36422         }
36423     },
36424     
36425     footer : {
36426         STANDARD : [
36427             {
36428                 tag : 'div',
36429                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36430                 action : 'rotate-left',
36431                 cn : [
36432                     {
36433                         tag : 'button',
36434                         cls : 'btn btn-default',
36435                         html : '<i class="fa fa-undo"></i>'
36436                     }
36437                 ]
36438             },
36439             {
36440                 tag : 'div',
36441                 cls : 'btn-group roo-upload-cropbox-picture',
36442                 action : 'picture',
36443                 cn : [
36444                     {
36445                         tag : 'button',
36446                         cls : 'btn btn-default',
36447                         html : '<i class="fa fa-picture-o"></i>'
36448                     }
36449                 ]
36450             },
36451             {
36452                 tag : 'div',
36453                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36454                 action : 'rotate-right',
36455                 cn : [
36456                     {
36457                         tag : 'button',
36458                         cls : 'btn btn-default',
36459                         html : '<i class="fa fa-repeat"></i>'
36460                     }
36461                 ]
36462             }
36463         ],
36464         DOCUMENT : [
36465             {
36466                 tag : 'div',
36467                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36468                 action : 'rotate-left',
36469                 cn : [
36470                     {
36471                         tag : 'button',
36472                         cls : 'btn btn-default',
36473                         html : '<i class="fa fa-undo"></i>'
36474                     }
36475                 ]
36476             },
36477             {
36478                 tag : 'div',
36479                 cls : 'btn-group roo-upload-cropbox-download',
36480                 action : 'download',
36481                 cn : [
36482                     {
36483                         tag : 'button',
36484                         cls : 'btn btn-default',
36485                         html : '<i class="fa fa-download"></i>'
36486                     }
36487                 ]
36488             },
36489             {
36490                 tag : 'div',
36491                 cls : 'btn-group roo-upload-cropbox-crop',
36492                 action : 'crop',
36493                 cn : [
36494                     {
36495                         tag : 'button',
36496                         cls : 'btn btn-default',
36497                         html : '<i class="fa fa-crop"></i>'
36498                     }
36499                 ]
36500             },
36501             {
36502                 tag : 'div',
36503                 cls : 'btn-group roo-upload-cropbox-trash',
36504                 action : 'trash',
36505                 cn : [
36506                     {
36507                         tag : 'button',
36508                         cls : 'btn btn-default',
36509                         html : '<i class="fa fa-trash"></i>'
36510                     }
36511                 ]
36512             },
36513             {
36514                 tag : 'div',
36515                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36516                 action : 'rotate-right',
36517                 cn : [
36518                     {
36519                         tag : 'button',
36520                         cls : 'btn btn-default',
36521                         html : '<i class="fa fa-repeat"></i>'
36522                     }
36523                 ]
36524             }
36525         ],
36526         ROTATOR : [
36527             {
36528                 tag : 'div',
36529                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36530                 action : 'rotate-left',
36531                 cn : [
36532                     {
36533                         tag : 'button',
36534                         cls : 'btn btn-default',
36535                         html : '<i class="fa fa-undo"></i>'
36536                     }
36537                 ]
36538             },
36539             {
36540                 tag : 'div',
36541                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36542                 action : 'rotate-right',
36543                 cn : [
36544                     {
36545                         tag : 'button',
36546                         cls : 'btn btn-default',
36547                         html : '<i class="fa fa-repeat"></i>'
36548                     }
36549                 ]
36550             }
36551         ]
36552     }
36553 });
36554
36555 /*
36556 * Licence: LGPL
36557 */
36558
36559 /**
36560  * @class Roo.bootstrap.DocumentManager
36561  * @extends Roo.bootstrap.Component
36562  * Bootstrap DocumentManager class
36563  * @cfg {String} paramName default 'imageUpload'
36564  * @cfg {String} toolTipName default 'filename'
36565  * @cfg {String} method default POST
36566  * @cfg {String} url action url
36567  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
36568  * @cfg {Boolean} multiple multiple upload default true
36569  * @cfg {Number} thumbSize default 300
36570  * @cfg {String} fieldLabel
36571  * @cfg {Number} labelWidth default 4
36572  * @cfg {String} labelAlign (left|top) default left
36573  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
36574 * @cfg {Number} labellg set the width of label (1-12)
36575  * @cfg {Number} labelmd set the width of label (1-12)
36576  * @cfg {Number} labelsm set the width of label (1-12)
36577  * @cfg {Number} labelxs set the width of label (1-12)
36578  * 
36579  * @constructor
36580  * Create a new DocumentManager
36581  * @param {Object} config The config object
36582  */
36583
36584 Roo.bootstrap.DocumentManager = function(config){
36585     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
36586     
36587     this.files = [];
36588     this.delegates = [];
36589     
36590     this.addEvents({
36591         /**
36592          * @event initial
36593          * Fire when initial the DocumentManager
36594          * @param {Roo.bootstrap.DocumentManager} this
36595          */
36596         "initial" : true,
36597         /**
36598          * @event inspect
36599          * inspect selected file
36600          * @param {Roo.bootstrap.DocumentManager} this
36601          * @param {File} file
36602          */
36603         "inspect" : true,
36604         /**
36605          * @event exception
36606          * Fire when xhr load exception
36607          * @param {Roo.bootstrap.DocumentManager} this
36608          * @param {XMLHttpRequest} xhr
36609          */
36610         "exception" : true,
36611         /**
36612          * @event afterupload
36613          * Fire when xhr load exception
36614          * @param {Roo.bootstrap.DocumentManager} this
36615          * @param {XMLHttpRequest} xhr
36616          */
36617         "afterupload" : true,
36618         /**
36619          * @event prepare
36620          * prepare the form data
36621          * @param {Roo.bootstrap.DocumentManager} this
36622          * @param {Object} formData
36623          */
36624         "prepare" : true,
36625         /**
36626          * @event remove
36627          * Fire when remove the file
36628          * @param {Roo.bootstrap.DocumentManager} this
36629          * @param {Object} file
36630          */
36631         "remove" : true,
36632         /**
36633          * @event refresh
36634          * Fire after refresh the file
36635          * @param {Roo.bootstrap.DocumentManager} this
36636          */
36637         "refresh" : true,
36638         /**
36639          * @event click
36640          * Fire after click the image
36641          * @param {Roo.bootstrap.DocumentManager} this
36642          * @param {Object} file
36643          */
36644         "click" : true,
36645         /**
36646          * @event edit
36647          * Fire when upload a image and editable set to true
36648          * @param {Roo.bootstrap.DocumentManager} this
36649          * @param {Object} file
36650          */
36651         "edit" : true,
36652         /**
36653          * @event beforeselectfile
36654          * Fire before select file
36655          * @param {Roo.bootstrap.DocumentManager} this
36656          */
36657         "beforeselectfile" : true,
36658         /**
36659          * @event process
36660          * Fire before process file
36661          * @param {Roo.bootstrap.DocumentManager} this
36662          * @param {Object} file
36663          */
36664         "process" : true,
36665         /**
36666          * @event previewrendered
36667          * Fire when preview rendered
36668          * @param {Roo.bootstrap.DocumentManager} this
36669          * @param {Object} file
36670          */
36671         "previewrendered" : true,
36672         /**
36673          */
36674         "previewResize" : true
36675         
36676     });
36677 };
36678
36679 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
36680     
36681     boxes : 0,
36682     inputName : '',
36683     thumbSize : 300,
36684     multiple : true,
36685     files : false,
36686     method : 'POST',
36687     url : '',
36688     paramName : 'imageUpload',
36689     toolTipName : 'filename',
36690     fieldLabel : '',
36691     labelWidth : 4,
36692     labelAlign : 'left',
36693     editable : true,
36694     delegates : false,
36695     xhr : false, 
36696     
36697     labellg : 0,
36698     labelmd : 0,
36699     labelsm : 0,
36700     labelxs : 0,
36701     
36702     getAutoCreate : function()
36703     {   
36704         var managerWidget = {
36705             tag : 'div',
36706             cls : 'roo-document-manager',
36707             cn : [
36708                 {
36709                     tag : 'input',
36710                     cls : 'roo-document-manager-selector',
36711                     type : 'file'
36712                 },
36713                 {
36714                     tag : 'div',
36715                     cls : 'roo-document-manager-uploader',
36716                     cn : [
36717                         {
36718                             tag : 'div',
36719                             cls : 'roo-document-manager-upload-btn',
36720                             html : '<i class="fa fa-plus"></i>'
36721                         }
36722                     ]
36723                     
36724                 }
36725             ]
36726         };
36727         
36728         var content = [
36729             {
36730                 tag : 'div',
36731                 cls : 'column col-md-12',
36732                 cn : managerWidget
36733             }
36734         ];
36735         
36736         if(this.fieldLabel.length){
36737             
36738             content = [
36739                 {
36740                     tag : 'div',
36741                     cls : 'column col-md-12',
36742                     html : this.fieldLabel
36743                 },
36744                 {
36745                     tag : 'div',
36746                     cls : 'column col-md-12',
36747                     cn : managerWidget
36748                 }
36749             ];
36750
36751             if(this.labelAlign == 'left'){
36752                 content = [
36753                     {
36754                         tag : 'div',
36755                         cls : 'column',
36756                         html : this.fieldLabel
36757                     },
36758                     {
36759                         tag : 'div',
36760                         cls : 'column',
36761                         cn : managerWidget
36762                     }
36763                 ];
36764                 
36765                 if(this.labelWidth > 12){
36766                     content[0].style = "width: " + this.labelWidth + 'px';
36767                 }
36768
36769                 if(this.labelWidth < 13 && this.labelmd == 0){
36770                     this.labelmd = this.labelWidth;
36771                 }
36772
36773                 if(this.labellg > 0){
36774                     content[0].cls += ' col-lg-' + this.labellg;
36775                     content[1].cls += ' col-lg-' + (12 - this.labellg);
36776                 }
36777
36778                 if(this.labelmd > 0){
36779                     content[0].cls += ' col-md-' + this.labelmd;
36780                     content[1].cls += ' col-md-' + (12 - this.labelmd);
36781                 }
36782
36783                 if(this.labelsm > 0){
36784                     content[0].cls += ' col-sm-' + this.labelsm;
36785                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
36786                 }
36787
36788                 if(this.labelxs > 0){
36789                     content[0].cls += ' col-xs-' + this.labelxs;
36790                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
36791                 }
36792                 
36793             }
36794         }
36795         
36796         var cfg = {
36797             tag : 'div',
36798             cls : 'row clearfix',
36799             cn : content
36800         };
36801         
36802         return cfg;
36803         
36804     },
36805     
36806     initEvents : function()
36807     {
36808         this.managerEl = this.el.select('.roo-document-manager', true).first();
36809         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36810         
36811         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
36812         this.selectorEl.hide();
36813         
36814         if(this.multiple){
36815             this.selectorEl.attr('multiple', 'multiple');
36816         }
36817         
36818         this.selectorEl.on('change', this.onFileSelected, this);
36819         
36820         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
36821         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36822         
36823         this.uploader.on('click', this.onUploaderClick, this);
36824         
36825         this.renderProgressDialog();
36826         
36827         var _this = this;
36828         
36829         window.addEventListener("resize", function() { _this.refresh(); } );
36830         
36831         this.fireEvent('initial', this);
36832     },
36833     
36834     renderProgressDialog : function()
36835     {
36836         var _this = this;
36837         
36838         this.progressDialog = new Roo.bootstrap.Modal({
36839             cls : 'roo-document-manager-progress-dialog',
36840             allow_close : false,
36841             animate : false,
36842             title : '',
36843             buttons : [
36844                 {
36845                     name  :'cancel',
36846                     weight : 'danger',
36847                     html : 'Cancel'
36848                 }
36849             ], 
36850             listeners : { 
36851                 btnclick : function() {
36852                     _this.uploadCancel();
36853                     this.hide();
36854                 }
36855             }
36856         });
36857          
36858         this.progressDialog.render(Roo.get(document.body));
36859          
36860         this.progress = new Roo.bootstrap.Progress({
36861             cls : 'roo-document-manager-progress',
36862             active : true,
36863             striped : true
36864         });
36865         
36866         this.progress.render(this.progressDialog.getChildContainer());
36867         
36868         this.progressBar = new Roo.bootstrap.ProgressBar({
36869             cls : 'roo-document-manager-progress-bar',
36870             aria_valuenow : 0,
36871             aria_valuemin : 0,
36872             aria_valuemax : 12,
36873             panel : 'success'
36874         });
36875         
36876         this.progressBar.render(this.progress.getChildContainer());
36877     },
36878     
36879     onUploaderClick : function(e)
36880     {
36881         e.preventDefault();
36882      
36883         if(this.fireEvent('beforeselectfile', this) != false){
36884             this.selectorEl.dom.click();
36885         }
36886         
36887     },
36888     
36889     onFileSelected : function(e)
36890     {
36891         e.preventDefault();
36892         
36893         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36894             return;
36895         }
36896         
36897         Roo.each(this.selectorEl.dom.files, function(file){
36898             if(this.fireEvent('inspect', this, file) != false){
36899                 this.files.push(file);
36900             }
36901         }, this);
36902         
36903         this.queue();
36904         
36905     },
36906     
36907     queue : function()
36908     {
36909         this.selectorEl.dom.value = '';
36910         
36911         if(!this.files || !this.files.length){
36912             return;
36913         }
36914         
36915         if(this.boxes > 0 && this.files.length > this.boxes){
36916             this.files = this.files.slice(0, this.boxes);
36917         }
36918         
36919         this.uploader.show();
36920         
36921         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36922             this.uploader.hide();
36923         }
36924         
36925         var _this = this;
36926         
36927         var files = [];
36928         
36929         var docs = [];
36930         
36931         Roo.each(this.files, function(file){
36932             
36933             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36934                 var f = this.renderPreview(file);
36935                 files.push(f);
36936                 return;
36937             }
36938             
36939             if(file.type.indexOf('image') != -1){
36940                 this.delegates.push(
36941                     (function(){
36942                         _this.process(file);
36943                     }).createDelegate(this)
36944                 );
36945         
36946                 return;
36947             }
36948             
36949             docs.push(
36950                 (function(){
36951                     _this.process(file);
36952                 }).createDelegate(this)
36953             );
36954             
36955         }, this);
36956         
36957         this.files = files;
36958         
36959         this.delegates = this.delegates.concat(docs);
36960         
36961         if(!this.delegates.length){
36962             this.refresh();
36963             return;
36964         }
36965         
36966         this.progressBar.aria_valuemax = this.delegates.length;
36967         
36968         this.arrange();
36969         
36970         return;
36971     },
36972     
36973     arrange : function()
36974     {
36975         if(!this.delegates.length){
36976             this.progressDialog.hide();
36977             this.refresh();
36978             return;
36979         }
36980         
36981         var delegate = this.delegates.shift();
36982         
36983         this.progressDialog.show();
36984         
36985         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
36986         
36987         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
36988         
36989         delegate();
36990     },
36991     
36992     refresh : function()
36993     {
36994         this.uploader.show();
36995         
36996         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36997             this.uploader.hide();
36998         }
36999         
37000         Roo.isTouch ? this.closable(false) : this.closable(true);
37001         
37002         this.fireEvent('refresh', this);
37003     },
37004     
37005     onRemove : function(e, el, o)
37006     {
37007         e.preventDefault();
37008         
37009         this.fireEvent('remove', this, o);
37010         
37011     },
37012     
37013     remove : function(o)
37014     {
37015         var files = [];
37016         
37017         Roo.each(this.files, function(file){
37018             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37019                 files.push(file);
37020                 return;
37021             }
37022
37023             o.target.remove();
37024
37025         }, this);
37026         
37027         this.files = files;
37028         
37029         this.refresh();
37030     },
37031     
37032     clear : function()
37033     {
37034         Roo.each(this.files, function(file){
37035             if(!file.target){
37036                 return;
37037             }
37038             
37039             file.target.remove();
37040
37041         }, this);
37042         
37043         this.files = [];
37044         
37045         this.refresh();
37046     },
37047     
37048     onClick : function(e, el, o)
37049     {
37050         e.preventDefault();
37051         
37052         this.fireEvent('click', this, o);
37053         
37054     },
37055     
37056     closable : function(closable)
37057     {
37058         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37059             
37060             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37061             
37062             if(closable){
37063                 el.show();
37064                 return;
37065             }
37066             
37067             el.hide();
37068             
37069         }, this);
37070     },
37071     
37072     xhrOnLoad : function(xhr)
37073     {
37074         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37075             el.remove();
37076         }, this);
37077         
37078         if (xhr.readyState !== 4) {
37079             this.arrange();
37080             this.fireEvent('exception', this, xhr);
37081             return;
37082         }
37083
37084         var response = Roo.decode(xhr.responseText);
37085         
37086         if(!response.success){
37087             this.arrange();
37088             this.fireEvent('exception', this, xhr);
37089             return;
37090         }
37091         
37092         var file = this.renderPreview(response.data);
37093         
37094         this.files.push(file);
37095         
37096         this.arrange();
37097         
37098         this.fireEvent('afterupload', this, xhr);
37099         
37100     },
37101     
37102     xhrOnError : function(xhr)
37103     {
37104         Roo.log('xhr on error');
37105         
37106         var response = Roo.decode(xhr.responseText);
37107           
37108         Roo.log(response);
37109         
37110         this.arrange();
37111     },
37112     
37113     process : function(file)
37114     {
37115         if(this.fireEvent('process', this, file) !== false){
37116             if(this.editable && file.type.indexOf('image') != -1){
37117                 this.fireEvent('edit', this, file);
37118                 return;
37119             }
37120
37121             this.uploadStart(file, false);
37122
37123             return;
37124         }
37125         
37126     },
37127     
37128     uploadStart : function(file, crop)
37129     {
37130         this.xhr = new XMLHttpRequest();
37131         
37132         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37133             this.arrange();
37134             return;
37135         }
37136         
37137         file.xhr = this.xhr;
37138             
37139         this.managerEl.createChild({
37140             tag : 'div',
37141             cls : 'roo-document-manager-loading',
37142             cn : [
37143                 {
37144                     tag : 'div',
37145                     tooltip : file.name,
37146                     cls : 'roo-document-manager-thumb',
37147                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37148                 }
37149             ]
37150
37151         });
37152
37153         this.xhr.open(this.method, this.url, true);
37154         
37155         var headers = {
37156             "Accept": "application/json",
37157             "Cache-Control": "no-cache",
37158             "X-Requested-With": "XMLHttpRequest"
37159         };
37160         
37161         for (var headerName in headers) {
37162             var headerValue = headers[headerName];
37163             if (headerValue) {
37164                 this.xhr.setRequestHeader(headerName, headerValue);
37165             }
37166         }
37167         
37168         var _this = this;
37169         
37170         this.xhr.onload = function()
37171         {
37172             _this.xhrOnLoad(_this.xhr);
37173         }
37174         
37175         this.xhr.onerror = function()
37176         {
37177             _this.xhrOnError(_this.xhr);
37178         }
37179         
37180         var formData = new FormData();
37181
37182         formData.append('returnHTML', 'NO');
37183         
37184         if(crop){
37185             formData.append('crop', crop);
37186         }
37187         
37188         formData.append(this.paramName, file, file.name);
37189         
37190         var options = {
37191             file : file, 
37192             manually : false
37193         };
37194         
37195         if(this.fireEvent('prepare', this, formData, options) != false){
37196             
37197             if(options.manually){
37198                 return;
37199             }
37200             
37201             this.xhr.send(formData);
37202             return;
37203         };
37204         
37205         this.uploadCancel();
37206     },
37207     
37208     uploadCancel : function()
37209     {
37210         if (this.xhr) {
37211             this.xhr.abort();
37212         }
37213         
37214         this.delegates = [];
37215         
37216         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37217             el.remove();
37218         }, this);
37219         
37220         this.arrange();
37221     },
37222     
37223     renderPreview : function(file)
37224     {
37225         if(typeof(file.target) != 'undefined' && file.target){
37226             return file;
37227         }
37228         
37229         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37230         
37231         var previewEl = this.managerEl.createChild({
37232             tag : 'div',
37233             cls : 'roo-document-manager-preview',
37234             cn : [
37235                 {
37236                     tag : 'div',
37237                     tooltip : file[this.toolTipName],
37238                     cls : 'roo-document-manager-thumb',
37239                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37240                 },
37241                 {
37242                     tag : 'button',
37243                     cls : 'close',
37244                     html : '<i class="fa fa-times-circle"></i>'
37245                 }
37246             ]
37247         });
37248
37249         var close = previewEl.select('button.close', true).first();
37250
37251         close.on('click', this.onRemove, this, file);
37252
37253         file.target = previewEl;
37254
37255         var image = previewEl.select('img', true).first();
37256         
37257         var _this = this;
37258         
37259         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37260         
37261         image.on('click', this.onClick, this, file);
37262         
37263         this.fireEvent('previewrendered', this, file);
37264         
37265         return file;
37266         
37267     },
37268     
37269     onPreviewLoad : function(file, image)
37270     {
37271         if(typeof(file.target) == 'undefined' || !file.target){
37272             return;
37273         }
37274         
37275         var width = image.dom.naturalWidth || image.dom.width;
37276         var height = image.dom.naturalHeight || image.dom.height;
37277         
37278         if(!this.previewResize) {
37279             return;
37280         }
37281         
37282         if(width > height){
37283             file.target.addClass('wide');
37284             return;
37285         }
37286         
37287         file.target.addClass('tall');
37288         return;
37289         
37290     },
37291     
37292     uploadFromSource : function(file, crop)
37293     {
37294         this.xhr = new XMLHttpRequest();
37295         
37296         this.managerEl.createChild({
37297             tag : 'div',
37298             cls : 'roo-document-manager-loading',
37299             cn : [
37300                 {
37301                     tag : 'div',
37302                     tooltip : file.name,
37303                     cls : 'roo-document-manager-thumb',
37304                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37305                 }
37306             ]
37307
37308         });
37309
37310         this.xhr.open(this.method, this.url, true);
37311         
37312         var headers = {
37313             "Accept": "application/json",
37314             "Cache-Control": "no-cache",
37315             "X-Requested-With": "XMLHttpRequest"
37316         };
37317         
37318         for (var headerName in headers) {
37319             var headerValue = headers[headerName];
37320             if (headerValue) {
37321                 this.xhr.setRequestHeader(headerName, headerValue);
37322             }
37323         }
37324         
37325         var _this = this;
37326         
37327         this.xhr.onload = function()
37328         {
37329             _this.xhrOnLoad(_this.xhr);
37330         }
37331         
37332         this.xhr.onerror = function()
37333         {
37334             _this.xhrOnError(_this.xhr);
37335         }
37336         
37337         var formData = new FormData();
37338
37339         formData.append('returnHTML', 'NO');
37340         
37341         formData.append('crop', crop);
37342         
37343         if(typeof(file.filename) != 'undefined'){
37344             formData.append('filename', file.filename);
37345         }
37346         
37347         if(typeof(file.mimetype) != 'undefined'){
37348             formData.append('mimetype', file.mimetype);
37349         }
37350         
37351         Roo.log(formData);
37352         
37353         if(this.fireEvent('prepare', this, formData) != false){
37354             this.xhr.send(formData);
37355         };
37356     }
37357 });
37358
37359 /*
37360 * Licence: LGPL
37361 */
37362
37363 /**
37364  * @class Roo.bootstrap.DocumentViewer
37365  * @extends Roo.bootstrap.Component
37366  * Bootstrap DocumentViewer class
37367  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37368  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37369  * 
37370  * @constructor
37371  * Create a new DocumentViewer
37372  * @param {Object} config The config object
37373  */
37374
37375 Roo.bootstrap.DocumentViewer = function(config){
37376     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37377     
37378     this.addEvents({
37379         /**
37380          * @event initial
37381          * Fire after initEvent
37382          * @param {Roo.bootstrap.DocumentViewer} this
37383          */
37384         "initial" : true,
37385         /**
37386          * @event click
37387          * Fire after click
37388          * @param {Roo.bootstrap.DocumentViewer} this
37389          */
37390         "click" : true,
37391         /**
37392          * @event download
37393          * Fire after download button
37394          * @param {Roo.bootstrap.DocumentViewer} this
37395          */
37396         "download" : true,
37397         /**
37398          * @event trash
37399          * Fire after trash button
37400          * @param {Roo.bootstrap.DocumentViewer} this
37401          */
37402         "trash" : true
37403         
37404     });
37405 };
37406
37407 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37408     
37409     showDownload : true,
37410     
37411     showTrash : true,
37412     
37413     getAutoCreate : function()
37414     {
37415         var cfg = {
37416             tag : 'div',
37417             cls : 'roo-document-viewer',
37418             cn : [
37419                 {
37420                     tag : 'div',
37421                     cls : 'roo-document-viewer-body',
37422                     cn : [
37423                         {
37424                             tag : 'div',
37425                             cls : 'roo-document-viewer-thumb',
37426                             cn : [
37427                                 {
37428                                     tag : 'img',
37429                                     cls : 'roo-document-viewer-image'
37430                                 }
37431                             ]
37432                         }
37433                     ]
37434                 },
37435                 {
37436                     tag : 'div',
37437                     cls : 'roo-document-viewer-footer',
37438                     cn : {
37439                         tag : 'div',
37440                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37441                         cn : [
37442                             {
37443                                 tag : 'div',
37444                                 cls : 'btn-group roo-document-viewer-download',
37445                                 cn : [
37446                                     {
37447                                         tag : 'button',
37448                                         cls : 'btn btn-default',
37449                                         html : '<i class="fa fa-download"></i>'
37450                                     }
37451                                 ]
37452                             },
37453                             {
37454                                 tag : 'div',
37455                                 cls : 'btn-group roo-document-viewer-trash',
37456                                 cn : [
37457                                     {
37458                                         tag : 'button',
37459                                         cls : 'btn btn-default',
37460                                         html : '<i class="fa fa-trash"></i>'
37461                                     }
37462                                 ]
37463                             }
37464                         ]
37465                     }
37466                 }
37467             ]
37468         };
37469         
37470         return cfg;
37471     },
37472     
37473     initEvents : function()
37474     {
37475         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37476         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37477         
37478         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37479         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37480         
37481         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37482         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37483         
37484         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37485         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37486         
37487         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37488         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37489         
37490         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37491         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37492         
37493         this.bodyEl.on('click', this.onClick, this);
37494         this.downloadBtn.on('click', this.onDownload, this);
37495         this.trashBtn.on('click', this.onTrash, this);
37496         
37497         this.downloadBtn.hide();
37498         this.trashBtn.hide();
37499         
37500         if(this.showDownload){
37501             this.downloadBtn.show();
37502         }
37503         
37504         if(this.showTrash){
37505             this.trashBtn.show();
37506         }
37507         
37508         if(!this.showDownload && !this.showTrash) {
37509             this.footerEl.hide();
37510         }
37511         
37512     },
37513     
37514     initial : function()
37515     {
37516         this.fireEvent('initial', this);
37517         
37518     },
37519     
37520     onClick : function(e)
37521     {
37522         e.preventDefault();
37523         
37524         this.fireEvent('click', this);
37525     },
37526     
37527     onDownload : function(e)
37528     {
37529         e.preventDefault();
37530         
37531         this.fireEvent('download', this);
37532     },
37533     
37534     onTrash : function(e)
37535     {
37536         e.preventDefault();
37537         
37538         this.fireEvent('trash', this);
37539     }
37540     
37541 });
37542 /*
37543  * - LGPL
37544  *
37545  * FieldLabel
37546  * 
37547  */
37548
37549 /**
37550  * @class Roo.bootstrap.form.FieldLabel
37551  * @extends Roo.bootstrap.Component
37552  * Bootstrap FieldLabel class
37553  * @cfg {String} html contents of the element
37554  * @cfg {String} tag tag of the element default label
37555  * @cfg {String} cls class of the element
37556  * @cfg {String} target label target 
37557  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
37558  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
37559  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
37560  * @cfg {String} iconTooltip default "This field is required"
37561  * @cfg {String} indicatorpos (left|right) default left
37562  * 
37563  * @constructor
37564  * Create a new FieldLabel
37565  * @param {Object} config The config object
37566  */
37567
37568 Roo.bootstrap.form.FieldLabel = function(config){
37569     Roo.bootstrap.Element.superclass.constructor.call(this, config);
37570     
37571     this.addEvents({
37572             /**
37573              * @event invalid
37574              * Fires after the field has been marked as invalid.
37575              * @param {Roo.form.FieldLabel} this
37576              * @param {String} msg The validation message
37577              */
37578             invalid : true,
37579             /**
37580              * @event valid
37581              * Fires after the field has been validated with no errors.
37582              * @param {Roo.form.FieldLabel} this
37583              */
37584             valid : true
37585         });
37586 };
37587
37588 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
37589     
37590     tag: 'label',
37591     cls: '',
37592     html: '',
37593     target: '',
37594     allowBlank : true,
37595     invalidClass : 'has-warning',
37596     validClass : 'has-success',
37597     iconTooltip : 'This field is required',
37598     indicatorpos : 'left',
37599     
37600     getAutoCreate : function(){
37601         
37602         var cls = "";
37603         if (!this.allowBlank) {
37604             cls  = "visible";
37605         }
37606         
37607         var cfg = {
37608             tag : this.tag,
37609             cls : 'roo-bootstrap-field-label ' + this.cls,
37610             for : this.target,
37611             cn : [
37612                 {
37613                     tag : 'i',
37614                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
37615                     tooltip : this.iconTooltip
37616                 },
37617                 {
37618                     tag : 'span',
37619                     html : this.html
37620                 }
37621             ] 
37622         };
37623         
37624         if(this.indicatorpos == 'right'){
37625             var cfg = {
37626                 tag : this.tag,
37627                 cls : 'roo-bootstrap-field-label ' + this.cls,
37628                 for : this.target,
37629                 cn : [
37630                     {
37631                         tag : 'span',
37632                         html : this.html
37633                     },
37634                     {
37635                         tag : 'i',
37636                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
37637                         tooltip : this.iconTooltip
37638                     }
37639                 ] 
37640             };
37641         }
37642         
37643         return cfg;
37644     },
37645     
37646     initEvents: function() 
37647     {
37648         Roo.bootstrap.Element.superclass.initEvents.call(this);
37649         
37650         this.indicator = this.indicatorEl();
37651         
37652         if(this.indicator){
37653             this.indicator.removeClass('visible');
37654             this.indicator.addClass('invisible');
37655         }
37656         
37657         Roo.bootstrap.form.FieldLabel.register(this);
37658     },
37659     
37660     indicatorEl : function()
37661     {
37662         var indicator = this.el.select('i.roo-required-indicator',true).first();
37663         
37664         if(!indicator){
37665             return false;
37666         }
37667         
37668         return indicator;
37669         
37670     },
37671     
37672     /**
37673      * Mark this field as valid
37674      */
37675     markValid : function()
37676     {
37677         if(this.indicator){
37678             this.indicator.removeClass('visible');
37679             this.indicator.addClass('invisible');
37680         }
37681         if (Roo.bootstrap.version == 3) {
37682             this.el.removeClass(this.invalidClass);
37683             this.el.addClass(this.validClass);
37684         } else {
37685             this.el.removeClass('is-invalid');
37686             this.el.addClass('is-valid');
37687         }
37688         
37689         
37690         this.fireEvent('valid', this);
37691     },
37692     
37693     /**
37694      * Mark this field as invalid
37695      * @param {String} msg The validation message
37696      */
37697     markInvalid : function(msg)
37698     {
37699         if(this.indicator){
37700             this.indicator.removeClass('invisible');
37701             this.indicator.addClass('visible');
37702         }
37703           if (Roo.bootstrap.version == 3) {
37704             this.el.removeClass(this.validClass);
37705             this.el.addClass(this.invalidClass);
37706         } else {
37707             this.el.removeClass('is-valid');
37708             this.el.addClass('is-invalid');
37709         }
37710         
37711         
37712         this.fireEvent('invalid', this, msg);
37713     }
37714     
37715    
37716 });
37717
37718 Roo.apply(Roo.bootstrap.form.FieldLabel, {
37719     
37720     groups: {},
37721     
37722      /**
37723     * register a FieldLabel Group
37724     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
37725     */
37726     register : function(label)
37727     {
37728         if(this.groups.hasOwnProperty(label.target)){
37729             return;
37730         }
37731      
37732         this.groups[label.target] = label;
37733         
37734     },
37735     /**
37736     * fetch a FieldLabel Group based on the target
37737     * @param {string} target
37738     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
37739     */
37740     get: function(target) {
37741         if (typeof(this.groups[target]) == 'undefined') {
37742             return false;
37743         }
37744         
37745         return this.groups[target] ;
37746     }
37747 });
37748
37749  
37750
37751  /*
37752  * - LGPL
37753  *
37754  * page DateSplitField.
37755  * 
37756  */
37757
37758
37759 /**
37760  * @class Roo.bootstrap.form.DateSplitField
37761  * @extends Roo.bootstrap.Component
37762  * Bootstrap DateSplitField class
37763  * @cfg {string} fieldLabel - the label associated
37764  * @cfg {Number} labelWidth set the width of label (0-12)
37765  * @cfg {String} labelAlign (top|left)
37766  * @cfg {Boolean} dayAllowBlank (true|false) default false
37767  * @cfg {Boolean} monthAllowBlank (true|false) default false
37768  * @cfg {Boolean} yearAllowBlank (true|false) default false
37769  * @cfg {string} dayPlaceholder 
37770  * @cfg {string} monthPlaceholder
37771  * @cfg {string} yearPlaceholder
37772  * @cfg {string} dayFormat default 'd'
37773  * @cfg {string} monthFormat default 'm'
37774  * @cfg {string} yearFormat default 'Y'
37775  * @cfg {Number} labellg set the width of label (1-12)
37776  * @cfg {Number} labelmd set the width of label (1-12)
37777  * @cfg {Number} labelsm set the width of label (1-12)
37778  * @cfg {Number} labelxs set the width of label (1-12)
37779
37780  *     
37781  * @constructor
37782  * Create a new DateSplitField
37783  * @param {Object} config The config object
37784  */
37785
37786 Roo.bootstrap.form.DateSplitField = function(config){
37787     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
37788     
37789     this.addEvents({
37790         // raw events
37791          /**
37792          * @event years
37793          * getting the data of years
37794          * @param {Roo.bootstrap.form.DateSplitField} this
37795          * @param {Object} years
37796          */
37797         "years" : true,
37798         /**
37799          * @event days
37800          * getting the data of days
37801          * @param {Roo.bootstrap.form.DateSplitField} this
37802          * @param {Object} days
37803          */
37804         "days" : true,
37805         /**
37806          * @event invalid
37807          * Fires after the field has been marked as invalid.
37808          * @param {Roo.form.Field} this
37809          * @param {String} msg The validation message
37810          */
37811         invalid : true,
37812        /**
37813          * @event valid
37814          * Fires after the field has been validated with no errors.
37815          * @param {Roo.form.Field} this
37816          */
37817         valid : true
37818     });
37819 };
37820
37821 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
37822     
37823     fieldLabel : '',
37824     labelAlign : 'top',
37825     labelWidth : 3,
37826     dayAllowBlank : false,
37827     monthAllowBlank : false,
37828     yearAllowBlank : false,
37829     dayPlaceholder : '',
37830     monthPlaceholder : '',
37831     yearPlaceholder : '',
37832     dayFormat : 'd',
37833     monthFormat : 'm',
37834     yearFormat : 'Y',
37835     isFormField : true,
37836     labellg : 0,
37837     labelmd : 0,
37838     labelsm : 0,
37839     labelxs : 0,
37840     
37841     getAutoCreate : function()
37842     {
37843         var cfg = {
37844             tag : 'div',
37845             cls : 'row roo-date-split-field-group',
37846             cn : [
37847                 {
37848                     tag : 'input',
37849                     type : 'hidden',
37850                     cls : 'form-hidden-field roo-date-split-field-group-value',
37851                     name : this.name
37852                 }
37853             ]
37854         };
37855         
37856         var labelCls = 'col-md-12';
37857         var contentCls = 'col-md-4';
37858         
37859         if(this.fieldLabel){
37860             
37861             var label = {
37862                 tag : 'div',
37863                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
37864                 cn : [
37865                     {
37866                         tag : 'label',
37867                         html : this.fieldLabel
37868                     }
37869                 ]
37870             };
37871             
37872             if(this.labelAlign == 'left'){
37873             
37874                 if(this.labelWidth > 12){
37875                     label.style = "width: " + this.labelWidth + 'px';
37876                 }
37877
37878                 if(this.labelWidth < 13 && this.labelmd == 0){
37879                     this.labelmd = this.labelWidth;
37880                 }
37881
37882                 if(this.labellg > 0){
37883                     labelCls = ' col-lg-' + this.labellg;
37884                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
37885                 }
37886
37887                 if(this.labelmd > 0){
37888                     labelCls = ' col-md-' + this.labelmd;
37889                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
37890                 }
37891
37892                 if(this.labelsm > 0){
37893                     labelCls = ' col-sm-' + this.labelsm;
37894                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
37895                 }
37896
37897                 if(this.labelxs > 0){
37898                     labelCls = ' col-xs-' + this.labelxs;
37899                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
37900                 }
37901             }
37902             
37903             label.cls += ' ' + labelCls;
37904             
37905             cfg.cn.push(label);
37906         }
37907         
37908         Roo.each(['day', 'month', 'year'], function(t){
37909             cfg.cn.push({
37910                 tag : 'div',
37911                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
37912             });
37913         }, this);
37914         
37915         return cfg;
37916     },
37917     
37918     inputEl: function ()
37919     {
37920         return this.el.select('.roo-date-split-field-group-value', true).first();
37921     },
37922     
37923     onRender : function(ct, position) 
37924     {
37925         var _this = this;
37926         
37927         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
37928         
37929         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
37930         
37931         this.dayField = new Roo.bootstrap.form.ComboBox({
37932             allowBlank : this.dayAllowBlank,
37933             alwaysQuery : true,
37934             displayField : 'value',
37935             editable : false,
37936             fieldLabel : '',
37937             forceSelection : true,
37938             mode : 'local',
37939             placeholder : this.dayPlaceholder,
37940             selectOnFocus : true,
37941             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37942             triggerAction : 'all',
37943             typeAhead : true,
37944             valueField : 'value',
37945             store : new Roo.data.SimpleStore({
37946                 data : (function() {    
37947                     var days = [];
37948                     _this.fireEvent('days', _this, days);
37949                     return days;
37950                 })(),
37951                 fields : [ 'value' ]
37952             }),
37953             listeners : {
37954                 select : function (_self, record, index)
37955                 {
37956                     _this.setValue(_this.getValue());
37957                 }
37958             }
37959         });
37960
37961         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
37962         
37963         this.monthField = new Roo.bootstrap.form.MonthField({
37964             after : '<i class=\"fa fa-calendar\"></i>',
37965             allowBlank : this.monthAllowBlank,
37966             placeholder : this.monthPlaceholder,
37967             readOnly : true,
37968             listeners : {
37969                 render : function (_self)
37970                 {
37971                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
37972                         e.preventDefault();
37973                         _self.focus();
37974                     });
37975                 },
37976                 select : function (_self, oldvalue, newvalue)
37977                 {
37978                     _this.setValue(_this.getValue());
37979                 }
37980             }
37981         });
37982         
37983         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
37984         
37985         this.yearField = new Roo.bootstrap.form.ComboBox({
37986             allowBlank : this.yearAllowBlank,
37987             alwaysQuery : true,
37988             displayField : 'value',
37989             editable : false,
37990             fieldLabel : '',
37991             forceSelection : true,
37992             mode : 'local',
37993             placeholder : this.yearPlaceholder,
37994             selectOnFocus : true,
37995             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37996             triggerAction : 'all',
37997             typeAhead : true,
37998             valueField : 'value',
37999             store : new Roo.data.SimpleStore({
38000                 data : (function() {
38001                     var years = [];
38002                     _this.fireEvent('years', _this, years);
38003                     return years;
38004                 })(),
38005                 fields : [ 'value' ]
38006             }),
38007             listeners : {
38008                 select : function (_self, record, index)
38009                 {
38010                     _this.setValue(_this.getValue());
38011                 }
38012             }
38013         });
38014
38015         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38016     },
38017     
38018     setValue : function(v, format)
38019     {
38020         this.inputEl.dom.value = v;
38021         
38022         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38023         
38024         var d = Date.parseDate(v, f);
38025         
38026         if(!d){
38027             this.validate();
38028             return;
38029         }
38030         
38031         this.setDay(d.format(this.dayFormat));
38032         this.setMonth(d.format(this.monthFormat));
38033         this.setYear(d.format(this.yearFormat));
38034         
38035         this.validate();
38036         
38037         return;
38038     },
38039     
38040     setDay : function(v)
38041     {
38042         this.dayField.setValue(v);
38043         this.inputEl.dom.value = this.getValue();
38044         this.validate();
38045         return;
38046     },
38047     
38048     setMonth : function(v)
38049     {
38050         this.monthField.setValue(v, true);
38051         this.inputEl.dom.value = this.getValue();
38052         this.validate();
38053         return;
38054     },
38055     
38056     setYear : function(v)
38057     {
38058         this.yearField.setValue(v);
38059         this.inputEl.dom.value = this.getValue();
38060         this.validate();
38061         return;
38062     },
38063     
38064     getDay : function()
38065     {
38066         return this.dayField.getValue();
38067     },
38068     
38069     getMonth : function()
38070     {
38071         return this.monthField.getValue();
38072     },
38073     
38074     getYear : function()
38075     {
38076         return this.yearField.getValue();
38077     },
38078     
38079     getValue : function()
38080     {
38081         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38082         
38083         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38084         
38085         return date;
38086     },
38087     
38088     reset : function()
38089     {
38090         this.setDay('');
38091         this.setMonth('');
38092         this.setYear('');
38093         this.inputEl.dom.value = '';
38094         this.validate();
38095         return;
38096     },
38097     
38098     validate : function()
38099     {
38100         var d = this.dayField.validate();
38101         var m = this.monthField.validate();
38102         var y = this.yearField.validate();
38103         
38104         var valid = true;
38105         
38106         if(
38107                 (!this.dayAllowBlank && !d) ||
38108                 (!this.monthAllowBlank && !m) ||
38109                 (!this.yearAllowBlank && !y)
38110         ){
38111             valid = false;
38112         }
38113         
38114         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38115             return valid;
38116         }
38117         
38118         if(valid){
38119             this.markValid();
38120             return valid;
38121         }
38122         
38123         this.markInvalid();
38124         
38125         return valid;
38126     },
38127     
38128     markValid : function()
38129     {
38130         
38131         var label = this.el.select('label', true).first();
38132         var icon = this.el.select('i.fa-star', true).first();
38133
38134         if(label && icon){
38135             icon.remove();
38136         }
38137         
38138         this.fireEvent('valid', this);
38139     },
38140     
38141      /**
38142      * Mark this field as invalid
38143      * @param {String} msg The validation message
38144      */
38145     markInvalid : function(msg)
38146     {
38147         
38148         var label = this.el.select('label', true).first();
38149         var icon = this.el.select('i.fa-star', true).first();
38150
38151         if(label && !icon){
38152             this.el.select('.roo-date-split-field-label', true).createChild({
38153                 tag : 'i',
38154                 cls : 'text-danger fa fa-lg fa-star',
38155                 tooltip : 'This field is required',
38156                 style : 'margin-right:5px;'
38157             }, label, true);
38158         }
38159         
38160         this.fireEvent('invalid', this, msg);
38161     },
38162     
38163     clearInvalid : function()
38164     {
38165         var label = this.el.select('label', true).first();
38166         var icon = this.el.select('i.fa-star', true).first();
38167
38168         if(label && icon){
38169             icon.remove();
38170         }
38171         
38172         this.fireEvent('valid', this);
38173     },
38174     
38175     getName: function()
38176     {
38177         return this.name;
38178     }
38179     
38180 });
38181
38182  
38183
38184 /**
38185  * @class Roo.bootstrap.LayoutMasonry
38186  * @extends Roo.bootstrap.Component
38187  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38188  * Bootstrap Layout Masonry class
38189  *
38190  * This is based on 
38191  * http://masonry.desandro.com
38192  *
38193  * The idea is to render all the bricks based on vertical width...
38194  *
38195  * The original code extends 'outlayer' - we might need to use that....
38196
38197  * @constructor
38198  * Create a new Element
38199  * @param {Object} config The config object
38200  */
38201
38202 Roo.bootstrap.LayoutMasonry = function(config){
38203     
38204     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38205     
38206     this.bricks = [];
38207     
38208     Roo.bootstrap.LayoutMasonry.register(this);
38209     
38210     this.addEvents({
38211         // raw events
38212         /**
38213          * @event layout
38214          * Fire after layout the items
38215          * @param {Roo.bootstrap.LayoutMasonry} this
38216          * @param {Roo.EventObject} e
38217          */
38218         "layout" : true
38219     });
38220     
38221 };
38222
38223 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38224     
38225     /**
38226      * @cfg {Boolean} isLayoutInstant = no animation?
38227      */   
38228     isLayoutInstant : false, // needed?
38229    
38230     /**
38231      * @cfg {Number} boxWidth  width of the columns
38232      */   
38233     boxWidth : 450,
38234     
38235       /**
38236      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38237      */   
38238     boxHeight : 0,
38239     
38240     /**
38241      * @cfg {Number} padWidth padding below box..
38242      */   
38243     padWidth : 10, 
38244     
38245     /**
38246      * @cfg {Number} gutter gutter width..
38247      */   
38248     gutter : 10,
38249     
38250      /**
38251      * @cfg {Number} maxCols maximum number of columns
38252      */   
38253     
38254     maxCols: 0,
38255     
38256     /**
38257      * @cfg {Boolean} isAutoInitial defalut true
38258      */   
38259     isAutoInitial : true, 
38260     
38261     containerWidth: 0,
38262     
38263     /**
38264      * @cfg {Boolean} isHorizontal defalut false
38265      */   
38266     isHorizontal : false, 
38267
38268     currentSize : null,
38269     
38270     tag: 'div',
38271     
38272     cls: '',
38273     
38274     bricks: null, //CompositeElement
38275     
38276     cols : 1,
38277     
38278     _isLayoutInited : false,
38279     
38280 //    isAlternative : false, // only use for vertical layout...
38281     
38282     /**
38283      * @cfg {Number} alternativePadWidth padding below box..
38284      */   
38285     alternativePadWidth : 50,
38286     
38287     selectedBrick : [],
38288     
38289     getAutoCreate : function(){
38290         
38291         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38292         
38293         var cfg = {
38294             tag: this.tag,
38295             cls: 'blog-masonary-wrapper ' + this.cls,
38296             cn : {
38297                 cls : 'mas-boxes masonary'
38298             }
38299         };
38300         
38301         return cfg;
38302     },
38303     
38304     getChildContainer: function( )
38305     {
38306         if (this.boxesEl) {
38307             return this.boxesEl;
38308         }
38309         
38310         this.boxesEl = this.el.select('.mas-boxes').first();
38311         
38312         return this.boxesEl;
38313     },
38314     
38315     
38316     initEvents : function()
38317     {
38318         var _this = this;
38319         
38320         if(this.isAutoInitial){
38321             Roo.log('hook children rendered');
38322             this.on('childrenrendered', function() {
38323                 Roo.log('children rendered');
38324                 _this.initial();
38325             } ,this);
38326         }
38327     },
38328     
38329     initial : function()
38330     {
38331         this.selectedBrick = [];
38332         
38333         this.currentSize = this.el.getBox(true);
38334         
38335         Roo.EventManager.onWindowResize(this.resize, this); 
38336
38337         if(!this.isAutoInitial){
38338             this.layout();
38339             return;
38340         }
38341         
38342         this.layout();
38343         
38344         return;
38345         //this.layout.defer(500,this);
38346         
38347     },
38348     
38349     resize : function()
38350     {
38351         var cs = this.el.getBox(true);
38352         
38353         if (
38354                 this.currentSize.width == cs.width && 
38355                 this.currentSize.x == cs.x && 
38356                 this.currentSize.height == cs.height && 
38357                 this.currentSize.y == cs.y 
38358         ) {
38359             Roo.log("no change in with or X or Y");
38360             return;
38361         }
38362         
38363         this.currentSize = cs;
38364         
38365         this.layout();
38366         
38367     },
38368     
38369     layout : function()
38370     {   
38371         this._resetLayout();
38372         
38373         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38374         
38375         this.layoutItems( isInstant );
38376       
38377         this._isLayoutInited = true;
38378         
38379         this.fireEvent('layout', this);
38380         
38381     },
38382     
38383     _resetLayout : function()
38384     {
38385         if(this.isHorizontal){
38386             this.horizontalMeasureColumns();
38387             return;
38388         }
38389         
38390         this.verticalMeasureColumns();
38391         
38392     },
38393     
38394     verticalMeasureColumns : function()
38395     {
38396         this.getContainerWidth();
38397         
38398 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38399 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38400 //            return;
38401 //        }
38402         
38403         var boxWidth = this.boxWidth + this.padWidth;
38404         
38405         if(this.containerWidth < this.boxWidth){
38406             boxWidth = this.containerWidth
38407         }
38408         
38409         var containerWidth = this.containerWidth;
38410         
38411         var cols = Math.floor(containerWidth / boxWidth);
38412         
38413         this.cols = Math.max( cols, 1 );
38414         
38415         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38416         
38417         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38418         
38419         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38420         
38421         this.colWidth = boxWidth + avail - this.padWidth;
38422         
38423         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38424         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38425     },
38426     
38427     horizontalMeasureColumns : function()
38428     {
38429         this.getContainerWidth();
38430         
38431         var boxWidth = this.boxWidth;
38432         
38433         if(this.containerWidth < boxWidth){
38434             boxWidth = this.containerWidth;
38435         }
38436         
38437         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38438         
38439         this.el.setHeight(boxWidth);
38440         
38441     },
38442     
38443     getContainerWidth : function()
38444     {
38445         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38446     },
38447     
38448     layoutItems : function( isInstant )
38449     {
38450         Roo.log(this.bricks);
38451         
38452         var items = Roo.apply([], this.bricks);
38453         
38454         if(this.isHorizontal){
38455             this._horizontalLayoutItems( items , isInstant );
38456             return;
38457         }
38458         
38459 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38460 //            this._verticalAlternativeLayoutItems( items , isInstant );
38461 //            return;
38462 //        }
38463         
38464         this._verticalLayoutItems( items , isInstant );
38465         
38466     },
38467     
38468     _verticalLayoutItems : function ( items , isInstant)
38469     {
38470         if ( !items || !items.length ) {
38471             return;
38472         }
38473         
38474         var standard = [
38475             ['xs', 'xs', 'xs', 'tall'],
38476             ['xs', 'xs', 'tall'],
38477             ['xs', 'xs', 'sm'],
38478             ['xs', 'xs', 'xs'],
38479             ['xs', 'tall'],
38480             ['xs', 'sm'],
38481             ['xs', 'xs'],
38482             ['xs'],
38483             
38484             ['sm', 'xs', 'xs'],
38485             ['sm', 'xs'],
38486             ['sm'],
38487             
38488             ['tall', 'xs', 'xs', 'xs'],
38489             ['tall', 'xs', 'xs'],
38490             ['tall', 'xs'],
38491             ['tall']
38492             
38493         ];
38494         
38495         var queue = [];
38496         
38497         var boxes = [];
38498         
38499         var box = [];
38500         
38501         Roo.each(items, function(item, k){
38502             
38503             switch (item.size) {
38504                 // these layouts take up a full box,
38505                 case 'md' :
38506                 case 'md-left' :
38507                 case 'md-right' :
38508                 case 'wide' :
38509                     
38510                     if(box.length){
38511                         boxes.push(box);
38512                         box = [];
38513                     }
38514                     
38515                     boxes.push([item]);
38516                     
38517                     break;
38518                     
38519                 case 'xs' :
38520                 case 'sm' :
38521                 case 'tall' :
38522                     
38523                     box.push(item);
38524                     
38525                     break;
38526                 default :
38527                     break;
38528                     
38529             }
38530             
38531         }, this);
38532         
38533         if(box.length){
38534             boxes.push(box);
38535             box = [];
38536         }
38537         
38538         var filterPattern = function(box, length)
38539         {
38540             if(!box.length){
38541                 return;
38542             }
38543             
38544             var match = false;
38545             
38546             var pattern = box.slice(0, length);
38547             
38548             var format = [];
38549             
38550             Roo.each(pattern, function(i){
38551                 format.push(i.size);
38552             }, this);
38553             
38554             Roo.each(standard, function(s){
38555                 
38556                 if(String(s) != String(format)){
38557                     return;
38558                 }
38559                 
38560                 match = true;
38561                 return false;
38562                 
38563             }, this);
38564             
38565             if(!match && length == 1){
38566                 return;
38567             }
38568             
38569             if(!match){
38570                 filterPattern(box, length - 1);
38571                 return;
38572             }
38573                 
38574             queue.push(pattern);
38575
38576             box = box.slice(length, box.length);
38577
38578             filterPattern(box, 4);
38579
38580             return;
38581             
38582         }
38583         
38584         Roo.each(boxes, function(box, k){
38585             
38586             if(!box.length){
38587                 return;
38588             }
38589             
38590             if(box.length == 1){
38591                 queue.push(box);
38592                 return;
38593             }
38594             
38595             filterPattern(box, 4);
38596             
38597         }, this);
38598         
38599         this._processVerticalLayoutQueue( queue, isInstant );
38600         
38601     },
38602     
38603 //    _verticalAlternativeLayoutItems : function( items , isInstant )
38604 //    {
38605 //        if ( !items || !items.length ) {
38606 //            return;
38607 //        }
38608 //
38609 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
38610 //        
38611 //    },
38612     
38613     _horizontalLayoutItems : function ( items , isInstant)
38614     {
38615         if ( !items || !items.length || items.length < 3) {
38616             return;
38617         }
38618         
38619         items.reverse();
38620         
38621         var eItems = items.slice(0, 3);
38622         
38623         items = items.slice(3, items.length);
38624         
38625         var standard = [
38626             ['xs', 'xs', 'xs', 'wide'],
38627             ['xs', 'xs', 'wide'],
38628             ['xs', 'xs', 'sm'],
38629             ['xs', 'xs', 'xs'],
38630             ['xs', 'wide'],
38631             ['xs', 'sm'],
38632             ['xs', 'xs'],
38633             ['xs'],
38634             
38635             ['sm', 'xs', 'xs'],
38636             ['sm', 'xs'],
38637             ['sm'],
38638             
38639             ['wide', 'xs', 'xs', 'xs'],
38640             ['wide', 'xs', 'xs'],
38641             ['wide', 'xs'],
38642             ['wide'],
38643             
38644             ['wide-thin']
38645         ];
38646         
38647         var queue = [];
38648         
38649         var boxes = [];
38650         
38651         var box = [];
38652         
38653         Roo.each(items, function(item, k){
38654             
38655             switch (item.size) {
38656                 case 'md' :
38657                 case 'md-left' :
38658                 case 'md-right' :
38659                 case 'tall' :
38660                     
38661                     if(box.length){
38662                         boxes.push(box);
38663                         box = [];
38664                     }
38665                     
38666                     boxes.push([item]);
38667                     
38668                     break;
38669                     
38670                 case 'xs' :
38671                 case 'sm' :
38672                 case 'wide' :
38673                 case 'wide-thin' :
38674                     
38675                     box.push(item);
38676                     
38677                     break;
38678                 default :
38679                     break;
38680                     
38681             }
38682             
38683         }, this);
38684         
38685         if(box.length){
38686             boxes.push(box);
38687             box = [];
38688         }
38689         
38690         var filterPattern = function(box, length)
38691         {
38692             if(!box.length){
38693                 return;
38694             }
38695             
38696             var match = false;
38697             
38698             var pattern = box.slice(0, length);
38699             
38700             var format = [];
38701             
38702             Roo.each(pattern, function(i){
38703                 format.push(i.size);
38704             }, this);
38705             
38706             Roo.each(standard, function(s){
38707                 
38708                 if(String(s) != String(format)){
38709                     return;
38710                 }
38711                 
38712                 match = true;
38713                 return false;
38714                 
38715             }, this);
38716             
38717             if(!match && length == 1){
38718                 return;
38719             }
38720             
38721             if(!match){
38722                 filterPattern(box, length - 1);
38723                 return;
38724             }
38725                 
38726             queue.push(pattern);
38727
38728             box = box.slice(length, box.length);
38729
38730             filterPattern(box, 4);
38731
38732             return;
38733             
38734         }
38735         
38736         Roo.each(boxes, function(box, k){
38737             
38738             if(!box.length){
38739                 return;
38740             }
38741             
38742             if(box.length == 1){
38743                 queue.push(box);
38744                 return;
38745             }
38746             
38747             filterPattern(box, 4);
38748             
38749         }, this);
38750         
38751         
38752         var prune = [];
38753         
38754         var pos = this.el.getBox(true);
38755         
38756         var minX = pos.x;
38757         
38758         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38759         
38760         var hit_end = false;
38761         
38762         Roo.each(queue, function(box){
38763             
38764             if(hit_end){
38765                 
38766                 Roo.each(box, function(b){
38767                 
38768                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38769                     b.el.hide();
38770
38771                 }, this);
38772
38773                 return;
38774             }
38775             
38776             var mx = 0;
38777             
38778             Roo.each(box, function(b){
38779                 
38780                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38781                 b.el.show();
38782
38783                 mx = Math.max(mx, b.x);
38784                 
38785             }, this);
38786             
38787             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
38788             
38789             if(maxX < minX){
38790                 
38791                 Roo.each(box, function(b){
38792                 
38793                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38794                     b.el.hide();
38795                     
38796                 }, this);
38797                 
38798                 hit_end = true;
38799                 
38800                 return;
38801             }
38802             
38803             prune.push(box);
38804             
38805         }, this);
38806         
38807         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
38808     },
38809     
38810     /** Sets position of item in DOM
38811     * @param {Element} item
38812     * @param {Number} x - horizontal position
38813     * @param {Number} y - vertical position
38814     * @param {Boolean} isInstant - disables transitions
38815     */
38816     _processVerticalLayoutQueue : function( queue, isInstant )
38817     {
38818         var pos = this.el.getBox(true);
38819         var x = pos.x;
38820         var y = pos.y;
38821         var maxY = [];
38822         
38823         for (var i = 0; i < this.cols; i++){
38824             maxY[i] = pos.y;
38825         }
38826         
38827         Roo.each(queue, function(box, k){
38828             
38829             var col = k % this.cols;
38830             
38831             Roo.each(box, function(b,kk){
38832                 
38833                 b.el.position('absolute');
38834                 
38835                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38836                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38837                 
38838                 if(b.size == 'md-left' || b.size == 'md-right'){
38839                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38840                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38841                 }
38842                 
38843                 b.el.setWidth(width);
38844                 b.el.setHeight(height);
38845                 // iframe?
38846                 b.el.select('iframe',true).setSize(width,height);
38847                 
38848             }, this);
38849             
38850             for (var i = 0; i < this.cols; i++){
38851                 
38852                 if(maxY[i] < maxY[col]){
38853                     col = i;
38854                     continue;
38855                 }
38856                 
38857                 col = Math.min(col, i);
38858                 
38859             }
38860             
38861             x = pos.x + col * (this.colWidth + this.padWidth);
38862             
38863             y = maxY[col];
38864             
38865             var positions = [];
38866             
38867             switch (box.length){
38868                 case 1 :
38869                     positions = this.getVerticalOneBoxColPositions(x, y, box);
38870                     break;
38871                 case 2 :
38872                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
38873                     break;
38874                 case 3 :
38875                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
38876                     break;
38877                 case 4 :
38878                     positions = this.getVerticalFourBoxColPositions(x, y, box);
38879                     break;
38880                 default :
38881                     break;
38882             }
38883             
38884             Roo.each(box, function(b,kk){
38885                 
38886                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38887                 
38888                 var sz = b.el.getSize();
38889                 
38890                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
38891                 
38892             }, this);
38893             
38894         }, this);
38895         
38896         var mY = 0;
38897         
38898         for (var i = 0; i < this.cols; i++){
38899             mY = Math.max(mY, maxY[i]);
38900         }
38901         
38902         this.el.setHeight(mY - pos.y);
38903         
38904     },
38905     
38906 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
38907 //    {
38908 //        var pos = this.el.getBox(true);
38909 //        var x = pos.x;
38910 //        var y = pos.y;
38911 //        var maxX = pos.right;
38912 //        
38913 //        var maxHeight = 0;
38914 //        
38915 //        Roo.each(items, function(item, k){
38916 //            
38917 //            var c = k % 2;
38918 //            
38919 //            item.el.position('absolute');
38920 //                
38921 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
38922 //
38923 //            item.el.setWidth(width);
38924 //
38925 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
38926 //
38927 //            item.el.setHeight(height);
38928 //            
38929 //            if(c == 0){
38930 //                item.el.setXY([x, y], isInstant ? false : true);
38931 //            } else {
38932 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
38933 //            }
38934 //            
38935 //            y = y + height + this.alternativePadWidth;
38936 //            
38937 //            maxHeight = maxHeight + height + this.alternativePadWidth;
38938 //            
38939 //        }, this);
38940 //        
38941 //        this.el.setHeight(maxHeight);
38942 //        
38943 //    },
38944     
38945     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
38946     {
38947         var pos = this.el.getBox(true);
38948         
38949         var minX = pos.x;
38950         var minY = pos.y;
38951         
38952         var maxX = pos.right;
38953         
38954         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
38955         
38956         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38957         
38958         Roo.each(queue, function(box, k){
38959             
38960             Roo.each(box, function(b, kk){
38961                 
38962                 b.el.position('absolute');
38963                 
38964                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38965                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38966                 
38967                 if(b.size == 'md-left' || b.size == 'md-right'){
38968                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38969                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38970                 }
38971                 
38972                 b.el.setWidth(width);
38973                 b.el.setHeight(height);
38974                 
38975             }, this);
38976             
38977             if(!box.length){
38978                 return;
38979             }
38980             
38981             var positions = [];
38982             
38983             switch (box.length){
38984                 case 1 :
38985                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
38986                     break;
38987                 case 2 :
38988                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
38989                     break;
38990                 case 3 :
38991                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
38992                     break;
38993                 case 4 :
38994                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
38995                     break;
38996                 default :
38997                     break;
38998             }
38999             
39000             Roo.each(box, function(b,kk){
39001                 
39002                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39003                 
39004                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39005                 
39006             }, this);
39007             
39008         }, this);
39009         
39010     },
39011     
39012     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39013     {
39014         Roo.each(eItems, function(b,k){
39015             
39016             b.size = (k == 0) ? 'sm' : 'xs';
39017             b.x = (k == 0) ? 2 : 1;
39018             b.y = (k == 0) ? 2 : 1;
39019             
39020             b.el.position('absolute');
39021             
39022             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39023                 
39024             b.el.setWidth(width);
39025             
39026             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39027             
39028             b.el.setHeight(height);
39029             
39030         }, this);
39031
39032         var positions = [];
39033         
39034         positions.push({
39035             x : maxX - this.unitWidth * 2 - this.gutter,
39036             y : minY
39037         });
39038         
39039         positions.push({
39040             x : maxX - this.unitWidth,
39041             y : minY + (this.unitWidth + this.gutter) * 2
39042         });
39043         
39044         positions.push({
39045             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39046             y : minY
39047         });
39048         
39049         Roo.each(eItems, function(b,k){
39050             
39051             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39052
39053         }, this);
39054         
39055     },
39056     
39057     getVerticalOneBoxColPositions : function(x, y, box)
39058     {
39059         var pos = [];
39060         
39061         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39062         
39063         if(box[0].size == 'md-left'){
39064             rand = 0;
39065         }
39066         
39067         if(box[0].size == 'md-right'){
39068             rand = 1;
39069         }
39070         
39071         pos.push({
39072             x : x + (this.unitWidth + this.gutter) * rand,
39073             y : y
39074         });
39075         
39076         return pos;
39077     },
39078     
39079     getVerticalTwoBoxColPositions : function(x, y, box)
39080     {
39081         var pos = [];
39082         
39083         if(box[0].size == 'xs'){
39084             
39085             pos.push({
39086                 x : x,
39087                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39088             });
39089
39090             pos.push({
39091                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39092                 y : y
39093             });
39094             
39095             return pos;
39096             
39097         }
39098         
39099         pos.push({
39100             x : x,
39101             y : y
39102         });
39103
39104         pos.push({
39105             x : x + (this.unitWidth + this.gutter) * 2,
39106             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39107         });
39108         
39109         return pos;
39110         
39111     },
39112     
39113     getVerticalThreeBoxColPositions : function(x, y, box)
39114     {
39115         var pos = [];
39116         
39117         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39118             
39119             pos.push({
39120                 x : x,
39121                 y : y
39122             });
39123
39124             pos.push({
39125                 x : x + (this.unitWidth + this.gutter) * 1,
39126                 y : y
39127             });
39128             
39129             pos.push({
39130                 x : x + (this.unitWidth + this.gutter) * 2,
39131                 y : y
39132             });
39133             
39134             return pos;
39135             
39136         }
39137         
39138         if(box[0].size == 'xs' && box[1].size == 'xs'){
39139             
39140             pos.push({
39141                 x : x,
39142                 y : y
39143             });
39144
39145             pos.push({
39146                 x : x,
39147                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39148             });
39149             
39150             pos.push({
39151                 x : x + (this.unitWidth + this.gutter) * 1,
39152                 y : y
39153             });
39154             
39155             return pos;
39156             
39157         }
39158         
39159         pos.push({
39160             x : x,
39161             y : y
39162         });
39163
39164         pos.push({
39165             x : x + (this.unitWidth + this.gutter) * 2,
39166             y : y
39167         });
39168
39169         pos.push({
39170             x : x + (this.unitWidth + this.gutter) * 2,
39171             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39172         });
39173             
39174         return pos;
39175         
39176     },
39177     
39178     getVerticalFourBoxColPositions : function(x, y, box)
39179     {
39180         var pos = [];
39181         
39182         if(box[0].size == 'xs'){
39183             
39184             pos.push({
39185                 x : x,
39186                 y : y
39187             });
39188
39189             pos.push({
39190                 x : x,
39191                 y : y + (this.unitHeight + this.gutter) * 1
39192             });
39193             
39194             pos.push({
39195                 x : x,
39196                 y : y + (this.unitHeight + this.gutter) * 2
39197             });
39198             
39199             pos.push({
39200                 x : x + (this.unitWidth + this.gutter) * 1,
39201                 y : y
39202             });
39203             
39204             return pos;
39205             
39206         }
39207         
39208         pos.push({
39209             x : x,
39210             y : y
39211         });
39212
39213         pos.push({
39214             x : x + (this.unitWidth + this.gutter) * 2,
39215             y : y
39216         });
39217
39218         pos.push({
39219             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39220             y : y + (this.unitHeight + this.gutter) * 1
39221         });
39222
39223         pos.push({
39224             x : x + (this.unitWidth + this.gutter) * 2,
39225             y : y + (this.unitWidth + this.gutter) * 2
39226         });
39227
39228         return pos;
39229         
39230     },
39231     
39232     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39233     {
39234         var pos = [];
39235         
39236         if(box[0].size == 'md-left'){
39237             pos.push({
39238                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39239                 y : minY
39240             });
39241             
39242             return pos;
39243         }
39244         
39245         if(box[0].size == 'md-right'){
39246             pos.push({
39247                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39248                 y : minY + (this.unitWidth + this.gutter) * 1
39249             });
39250             
39251             return pos;
39252         }
39253         
39254         var rand = Math.floor(Math.random() * (4 - box[0].y));
39255         
39256         pos.push({
39257             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39258             y : minY + (this.unitWidth + this.gutter) * rand
39259         });
39260         
39261         return pos;
39262         
39263     },
39264     
39265     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39266     {
39267         var pos = [];
39268         
39269         if(box[0].size == 'xs'){
39270             
39271             pos.push({
39272                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39273                 y : minY
39274             });
39275
39276             pos.push({
39277                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39278                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39279             });
39280             
39281             return pos;
39282             
39283         }
39284         
39285         pos.push({
39286             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39287             y : minY
39288         });
39289
39290         pos.push({
39291             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39292             y : minY + (this.unitWidth + this.gutter) * 2
39293         });
39294         
39295         return pos;
39296         
39297     },
39298     
39299     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39300     {
39301         var pos = [];
39302         
39303         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39304             
39305             pos.push({
39306                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39307                 y : minY
39308             });
39309
39310             pos.push({
39311                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39312                 y : minY + (this.unitWidth + this.gutter) * 1
39313             });
39314             
39315             pos.push({
39316                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39317                 y : minY + (this.unitWidth + this.gutter) * 2
39318             });
39319             
39320             return pos;
39321             
39322         }
39323         
39324         if(box[0].size == 'xs' && box[1].size == 'xs'){
39325             
39326             pos.push({
39327                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39328                 y : minY
39329             });
39330
39331             pos.push({
39332                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39333                 y : minY
39334             });
39335             
39336             pos.push({
39337                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39338                 y : minY + (this.unitWidth + this.gutter) * 1
39339             });
39340             
39341             return pos;
39342             
39343         }
39344         
39345         pos.push({
39346             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39347             y : minY
39348         });
39349
39350         pos.push({
39351             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39352             y : minY + (this.unitWidth + this.gutter) * 2
39353         });
39354
39355         pos.push({
39356             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39357             y : minY + (this.unitWidth + this.gutter) * 2
39358         });
39359             
39360         return pos;
39361         
39362     },
39363     
39364     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39365     {
39366         var pos = [];
39367         
39368         if(box[0].size == 'xs'){
39369             
39370             pos.push({
39371                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39372                 y : minY
39373             });
39374
39375             pos.push({
39376                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39377                 y : minY
39378             });
39379             
39380             pos.push({
39381                 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),
39382                 y : minY
39383             });
39384             
39385             pos.push({
39386                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39387                 y : minY + (this.unitWidth + this.gutter) * 1
39388             });
39389             
39390             return pos;
39391             
39392         }
39393         
39394         pos.push({
39395             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39396             y : minY
39397         });
39398         
39399         pos.push({
39400             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39401             y : minY + (this.unitWidth + this.gutter) * 2
39402         });
39403         
39404         pos.push({
39405             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39406             y : minY + (this.unitWidth + this.gutter) * 2
39407         });
39408         
39409         pos.push({
39410             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),
39411             y : minY + (this.unitWidth + this.gutter) * 2
39412         });
39413
39414         return pos;
39415         
39416     },
39417     
39418     /**
39419     * remove a Masonry Brick
39420     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39421     */
39422     removeBrick : function(brick_id)
39423     {
39424         if (!brick_id) {
39425             return;
39426         }
39427         
39428         for (var i = 0; i<this.bricks.length; i++) {
39429             if (this.bricks[i].id == brick_id) {
39430                 this.bricks.splice(i,1);
39431                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39432                 this.initial();
39433             }
39434         }
39435     },
39436     
39437     /**
39438     * adds a Masonry Brick
39439     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39440     */
39441     addBrick : function(cfg)
39442     {
39443         var cn = new Roo.bootstrap.MasonryBrick(cfg);
39444         //this.register(cn);
39445         cn.parentId = this.id;
39446         cn.render(this.el);
39447         return cn;
39448     },
39449     
39450     /**
39451     * register a Masonry Brick
39452     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39453     */
39454     
39455     register : function(brick)
39456     {
39457         this.bricks.push(brick);
39458         brick.masonryId = this.id;
39459     },
39460     
39461     /**
39462     * clear all the Masonry Brick
39463     */
39464     clearAll : function()
39465     {
39466         this.bricks = [];
39467         //this.getChildContainer().dom.innerHTML = "";
39468         this.el.dom.innerHTML = '';
39469     },
39470     
39471     getSelected : function()
39472     {
39473         if (!this.selectedBrick) {
39474             return false;
39475         }
39476         
39477         return this.selectedBrick;
39478     }
39479 });
39480
39481 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39482     
39483     groups: {},
39484      /**
39485     * register a Masonry Layout
39486     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39487     */
39488     
39489     register : function(layout)
39490     {
39491         this.groups[layout.id] = layout;
39492     },
39493     /**
39494     * fetch a  Masonry Layout based on the masonry layout ID
39495     * @param {string} the masonry layout to add
39496     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39497     */
39498     
39499     get: function(layout_id) {
39500         if (typeof(this.groups[layout_id]) == 'undefined') {
39501             return false;
39502         }
39503         return this.groups[layout_id] ;
39504     }
39505     
39506     
39507     
39508 });
39509
39510  
39511
39512  /**
39513  *
39514  * This is based on 
39515  * http://masonry.desandro.com
39516  *
39517  * The idea is to render all the bricks based on vertical width...
39518  *
39519  * The original code extends 'outlayer' - we might need to use that....
39520  * 
39521  */
39522
39523
39524 /**
39525  * @class Roo.bootstrap.LayoutMasonryAuto
39526  * @extends Roo.bootstrap.Component
39527  * Bootstrap Layout Masonry class
39528  * 
39529  * @constructor
39530  * Create a new Element
39531  * @param {Object} config The config object
39532  */
39533
39534 Roo.bootstrap.LayoutMasonryAuto = function(config){
39535     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39536 };
39537
39538 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
39539     
39540       /**
39541      * @cfg {Boolean} isFitWidth  - resize the width..
39542      */   
39543     isFitWidth : false,  // options..
39544     /**
39545      * @cfg {Boolean} isOriginLeft = left align?
39546      */   
39547     isOriginLeft : true,
39548     /**
39549      * @cfg {Boolean} isOriginTop = top align?
39550      */   
39551     isOriginTop : false,
39552     /**
39553      * @cfg {Boolean} isLayoutInstant = no animation?
39554      */   
39555     isLayoutInstant : false, // needed?
39556     /**
39557      * @cfg {Boolean} isResizingContainer = not sure if this is used..
39558      */   
39559     isResizingContainer : true,
39560     /**
39561      * @cfg {Number} columnWidth  width of the columns 
39562      */   
39563     
39564     columnWidth : 0,
39565     
39566     /**
39567      * @cfg {Number} maxCols maximum number of columns
39568      */   
39569     
39570     maxCols: 0,
39571     /**
39572      * @cfg {Number} padHeight padding below box..
39573      */   
39574     
39575     padHeight : 10, 
39576     
39577     /**
39578      * @cfg {Boolean} isAutoInitial defalut true
39579      */   
39580     
39581     isAutoInitial : true, 
39582     
39583     // private?
39584     gutter : 0,
39585     
39586     containerWidth: 0,
39587     initialColumnWidth : 0,
39588     currentSize : null,
39589     
39590     colYs : null, // array.
39591     maxY : 0,
39592     padWidth: 10,
39593     
39594     
39595     tag: 'div',
39596     cls: '',
39597     bricks: null, //CompositeElement
39598     cols : 0, // array?
39599     // element : null, // wrapped now this.el
39600     _isLayoutInited : null, 
39601     
39602     
39603     getAutoCreate : function(){
39604         
39605         var cfg = {
39606             tag: this.tag,
39607             cls: 'blog-masonary-wrapper ' + this.cls,
39608             cn : {
39609                 cls : 'mas-boxes masonary'
39610             }
39611         };
39612         
39613         return cfg;
39614     },
39615     
39616     getChildContainer: function( )
39617     {
39618         if (this.boxesEl) {
39619             return this.boxesEl;
39620         }
39621         
39622         this.boxesEl = this.el.select('.mas-boxes').first();
39623         
39624         return this.boxesEl;
39625     },
39626     
39627     
39628     initEvents : function()
39629     {
39630         var _this = this;
39631         
39632         if(this.isAutoInitial){
39633             Roo.log('hook children rendered');
39634             this.on('childrenrendered', function() {
39635                 Roo.log('children rendered');
39636                 _this.initial();
39637             } ,this);
39638         }
39639         
39640     },
39641     
39642     initial : function()
39643     {
39644         this.reloadItems();
39645
39646         this.currentSize = this.el.getBox(true);
39647
39648         /// was window resize... - let's see if this works..
39649         Roo.EventManager.onWindowResize(this.resize, this); 
39650
39651         if(!this.isAutoInitial){
39652             this.layout();
39653             return;
39654         }
39655         
39656         this.layout.defer(500,this);
39657     },
39658     
39659     reloadItems: function()
39660     {
39661         this.bricks = this.el.select('.masonry-brick', true);
39662         
39663         this.bricks.each(function(b) {
39664             //Roo.log(b.getSize());
39665             if (!b.attr('originalwidth')) {
39666                 b.attr('originalwidth',  b.getSize().width);
39667             }
39668             
39669         });
39670         
39671         Roo.log(this.bricks.elements.length);
39672     },
39673     
39674     resize : function()
39675     {
39676         Roo.log('resize');
39677         var cs = this.el.getBox(true);
39678         
39679         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
39680             Roo.log("no change in with or X");
39681             return;
39682         }
39683         this.currentSize = cs;
39684         this.layout();
39685     },
39686     
39687     layout : function()
39688     {
39689          Roo.log('layout');
39690         this._resetLayout();
39691         //this._manageStamps();
39692       
39693         // don't animate first layout
39694         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39695         this.layoutItems( isInstant );
39696       
39697         // flag for initalized
39698         this._isLayoutInited = true;
39699     },
39700     
39701     layoutItems : function( isInstant )
39702     {
39703         //var items = this._getItemsForLayout( this.items );
39704         // original code supports filtering layout items.. we just ignore it..
39705         
39706         this._layoutItems( this.bricks , isInstant );
39707       
39708         this._postLayout();
39709     },
39710     _layoutItems : function ( items , isInstant)
39711     {
39712        //this.fireEvent( 'layout', this, items );
39713     
39714
39715         if ( !items || !items.elements.length ) {
39716           // no items, emit event with empty array
39717             return;
39718         }
39719
39720         var queue = [];
39721         items.each(function(item) {
39722             Roo.log("layout item");
39723             Roo.log(item);
39724             // get x/y object from method
39725             var position = this._getItemLayoutPosition( item );
39726             // enqueue
39727             position.item = item;
39728             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
39729             queue.push( position );
39730         }, this);
39731       
39732         this._processLayoutQueue( queue );
39733     },
39734     /** Sets position of item in DOM
39735     * @param {Element} item
39736     * @param {Number} x - horizontal position
39737     * @param {Number} y - vertical position
39738     * @param {Boolean} isInstant - disables transitions
39739     */
39740     _processLayoutQueue : function( queue )
39741     {
39742         for ( var i=0, len = queue.length; i < len; i++ ) {
39743             var obj = queue[i];
39744             obj.item.position('absolute');
39745             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
39746         }
39747     },
39748       
39749     
39750     /**
39751     * Any logic you want to do after each layout,
39752     * i.e. size the container
39753     */
39754     _postLayout : function()
39755     {
39756         this.resizeContainer();
39757     },
39758     
39759     resizeContainer : function()
39760     {
39761         if ( !this.isResizingContainer ) {
39762             return;
39763         }
39764         var size = this._getContainerSize();
39765         if ( size ) {
39766             this.el.setSize(size.width,size.height);
39767             this.boxesEl.setSize(size.width,size.height);
39768         }
39769     },
39770     
39771     
39772     
39773     _resetLayout : function()
39774     {
39775         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
39776         this.colWidth = this.el.getWidth();
39777         //this.gutter = this.el.getWidth(); 
39778         
39779         this.measureColumns();
39780
39781         // reset column Y
39782         var i = this.cols;
39783         this.colYs = [];
39784         while (i--) {
39785             this.colYs.push( 0 );
39786         }
39787     
39788         this.maxY = 0;
39789     },
39790
39791     measureColumns : function()
39792     {
39793         this.getContainerWidth();
39794       // if columnWidth is 0, default to outerWidth of first item
39795         if ( !this.columnWidth ) {
39796             var firstItem = this.bricks.first();
39797             Roo.log(firstItem);
39798             this.columnWidth  = this.containerWidth;
39799             if (firstItem && firstItem.attr('originalwidth') ) {
39800                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
39801             }
39802             // columnWidth fall back to item of first element
39803             Roo.log("set column width?");
39804                         this.initialColumnWidth = this.columnWidth  ;
39805
39806             // if first elem has no width, default to size of container
39807             
39808         }
39809         
39810         
39811         if (this.initialColumnWidth) {
39812             this.columnWidth = this.initialColumnWidth;
39813         }
39814         
39815         
39816             
39817         // column width is fixed at the top - however if container width get's smaller we should
39818         // reduce it...
39819         
39820         // this bit calcs how man columns..
39821             
39822         var columnWidth = this.columnWidth += this.gutter;
39823       
39824         // calculate columns
39825         var containerWidth = this.containerWidth + this.gutter;
39826         
39827         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
39828         // fix rounding errors, typically with gutters
39829         var excess = columnWidth - containerWidth % columnWidth;
39830         
39831         
39832         // if overshoot is less than a pixel, round up, otherwise floor it
39833         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
39834         cols = Math[ mathMethod ]( cols );
39835         this.cols = Math.max( cols, 1 );
39836         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39837         
39838          // padding positioning..
39839         var totalColWidth = this.cols * this.columnWidth;
39840         var padavail = this.containerWidth - totalColWidth;
39841         // so for 2 columns - we need 3 'pads'
39842         
39843         var padNeeded = (1+this.cols) * this.padWidth;
39844         
39845         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
39846         
39847         this.columnWidth += padExtra
39848         //this.padWidth = Math.floor(padavail /  ( this.cols));
39849         
39850         // adjust colum width so that padding is fixed??
39851         
39852         // we have 3 columns ... total = width * 3
39853         // we have X left over... that should be used by 
39854         
39855         //if (this.expandC) {
39856             
39857         //}
39858         
39859         
39860         
39861     },
39862     
39863     getContainerWidth : function()
39864     {
39865        /* // container is parent if fit width
39866         var container = this.isFitWidth ? this.element.parentNode : this.element;
39867         // check that this.size and size are there
39868         // IE8 triggers resize on body size change, so they might not be
39869         
39870         var size = getSize( container );  //FIXME
39871         this.containerWidth = size && size.innerWidth; //FIXME
39872         */
39873          
39874         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39875         
39876     },
39877     
39878     _getItemLayoutPosition : function( item )  // what is item?
39879     {
39880         // we resize the item to our columnWidth..
39881       
39882         item.setWidth(this.columnWidth);
39883         item.autoBoxAdjust  = false;
39884         
39885         var sz = item.getSize();
39886  
39887         // how many columns does this brick span
39888         var remainder = this.containerWidth % this.columnWidth;
39889         
39890         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
39891         // round if off by 1 pixel, otherwise use ceil
39892         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
39893         colSpan = Math.min( colSpan, this.cols );
39894         
39895         // normally this should be '1' as we dont' currently allow multi width columns..
39896         
39897         var colGroup = this._getColGroup( colSpan );
39898         // get the minimum Y value from the columns
39899         var minimumY = Math.min.apply( Math, colGroup );
39900         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39901         
39902         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
39903          
39904         // position the brick
39905         var position = {
39906             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
39907             y: this.currentSize.y + minimumY + this.padHeight
39908         };
39909         
39910         Roo.log(position);
39911         // apply setHeight to necessary columns
39912         var setHeight = minimumY + sz.height + this.padHeight;
39913         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39914         
39915         var setSpan = this.cols + 1 - colGroup.length;
39916         for ( var i = 0; i < setSpan; i++ ) {
39917           this.colYs[ shortColIndex + i ] = setHeight ;
39918         }
39919       
39920         return position;
39921     },
39922     
39923     /**
39924      * @param {Number} colSpan - number of columns the element spans
39925      * @returns {Array} colGroup
39926      */
39927     _getColGroup : function( colSpan )
39928     {
39929         if ( colSpan < 2 ) {
39930           // if brick spans only one column, use all the column Ys
39931           return this.colYs;
39932         }
39933       
39934         var colGroup = [];
39935         // how many different places could this brick fit horizontally
39936         var groupCount = this.cols + 1 - colSpan;
39937         // for each group potential horizontal position
39938         for ( var i = 0; i < groupCount; i++ ) {
39939           // make an array of colY values for that one group
39940           var groupColYs = this.colYs.slice( i, i + colSpan );
39941           // and get the max value of the array
39942           colGroup[i] = Math.max.apply( Math, groupColYs );
39943         }
39944         return colGroup;
39945     },
39946     /*
39947     _manageStamp : function( stamp )
39948     {
39949         var stampSize =  stamp.getSize();
39950         var offset = stamp.getBox();
39951         // get the columns that this stamp affects
39952         var firstX = this.isOriginLeft ? offset.x : offset.right;
39953         var lastX = firstX + stampSize.width;
39954         var firstCol = Math.floor( firstX / this.columnWidth );
39955         firstCol = Math.max( 0, firstCol );
39956         
39957         var lastCol = Math.floor( lastX / this.columnWidth );
39958         // lastCol should not go over if multiple of columnWidth #425
39959         lastCol -= lastX % this.columnWidth ? 0 : 1;
39960         lastCol = Math.min( this.cols - 1, lastCol );
39961         
39962         // set colYs to bottom of the stamp
39963         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
39964             stampSize.height;
39965             
39966         for ( var i = firstCol; i <= lastCol; i++ ) {
39967           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
39968         }
39969     },
39970     */
39971     
39972     _getContainerSize : function()
39973     {
39974         this.maxY = Math.max.apply( Math, this.colYs );
39975         var size = {
39976             height: this.maxY
39977         };
39978       
39979         if ( this.isFitWidth ) {
39980             size.width = this._getContainerFitWidth();
39981         }
39982       
39983         return size;
39984     },
39985     
39986     _getContainerFitWidth : function()
39987     {
39988         var unusedCols = 0;
39989         // count unused columns
39990         var i = this.cols;
39991         while ( --i ) {
39992           if ( this.colYs[i] !== 0 ) {
39993             break;
39994           }
39995           unusedCols++;
39996         }
39997         // fit container to columns that have been used
39998         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
39999     },
40000     
40001     needsResizeLayout : function()
40002     {
40003         var previousWidth = this.containerWidth;
40004         this.getContainerWidth();
40005         return previousWidth !== this.containerWidth;
40006     }
40007  
40008 });
40009
40010  
40011
40012  /*
40013  * - LGPL
40014  *
40015  * element
40016  * 
40017  */
40018
40019 /**
40020  * @class Roo.bootstrap.MasonryBrick
40021  * @extends Roo.bootstrap.Component
40022  * Bootstrap MasonryBrick class
40023  * 
40024  * @constructor
40025  * Create a new MasonryBrick
40026  * @param {Object} config The config object
40027  */
40028
40029 Roo.bootstrap.MasonryBrick = function(config){
40030     
40031     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40032     
40033     Roo.bootstrap.MasonryBrick.register(this);
40034     
40035     this.addEvents({
40036         // raw events
40037         /**
40038          * @event click
40039          * When a MasonryBrick is clcik
40040          * @param {Roo.bootstrap.MasonryBrick} this
40041          * @param {Roo.EventObject} e
40042          */
40043         "click" : true
40044     });
40045 };
40046
40047 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40048     
40049     /**
40050      * @cfg {String} title
40051      */   
40052     title : '',
40053     /**
40054      * @cfg {String} html
40055      */   
40056     html : '',
40057     /**
40058      * @cfg {String} bgimage
40059      */   
40060     bgimage : '',
40061     /**
40062      * @cfg {String} videourl
40063      */   
40064     videourl : '',
40065     /**
40066      * @cfg {String} cls
40067      */   
40068     cls : '',
40069     /**
40070      * @cfg {String} href
40071      */   
40072     href : '',
40073     /**
40074      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40075      */   
40076     size : 'xs',
40077     
40078     /**
40079      * @cfg {String} placetitle (center|bottom)
40080      */   
40081     placetitle : '',
40082     
40083     /**
40084      * @cfg {Boolean} isFitContainer defalut true
40085      */   
40086     isFitContainer : true, 
40087     
40088     /**
40089      * @cfg {Boolean} preventDefault defalut false
40090      */   
40091     preventDefault : false, 
40092     
40093     /**
40094      * @cfg {Boolean} inverse defalut false
40095      */   
40096     maskInverse : false, 
40097     
40098     getAutoCreate : function()
40099     {
40100         if(!this.isFitContainer){
40101             return this.getSplitAutoCreate();
40102         }
40103         
40104         var cls = 'masonry-brick masonry-brick-full';
40105         
40106         if(this.href.length){
40107             cls += ' masonry-brick-link';
40108         }
40109         
40110         if(this.bgimage.length){
40111             cls += ' masonry-brick-image';
40112         }
40113         
40114         if(this.maskInverse){
40115             cls += ' mask-inverse';
40116         }
40117         
40118         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40119             cls += ' enable-mask';
40120         }
40121         
40122         if(this.size){
40123             cls += ' masonry-' + this.size + '-brick';
40124         }
40125         
40126         if(this.placetitle.length){
40127             
40128             switch (this.placetitle) {
40129                 case 'center' :
40130                     cls += ' masonry-center-title';
40131                     break;
40132                 case 'bottom' :
40133                     cls += ' masonry-bottom-title';
40134                     break;
40135                 default:
40136                     break;
40137             }
40138             
40139         } else {
40140             if(!this.html.length && !this.bgimage.length){
40141                 cls += ' masonry-center-title';
40142             }
40143
40144             if(!this.html.length && this.bgimage.length){
40145                 cls += ' masonry-bottom-title';
40146             }
40147         }
40148         
40149         if(this.cls){
40150             cls += ' ' + this.cls;
40151         }
40152         
40153         var cfg = {
40154             tag: (this.href.length) ? 'a' : 'div',
40155             cls: cls,
40156             cn: [
40157                 {
40158                     tag: 'div',
40159                     cls: 'masonry-brick-mask'
40160                 },
40161                 {
40162                     tag: 'div',
40163                     cls: 'masonry-brick-paragraph',
40164                     cn: []
40165                 }
40166             ]
40167         };
40168         
40169         if(this.href.length){
40170             cfg.href = this.href;
40171         }
40172         
40173         var cn = cfg.cn[1].cn;
40174         
40175         if(this.title.length){
40176             cn.push({
40177                 tag: 'h4',
40178                 cls: 'masonry-brick-title',
40179                 html: this.title
40180             });
40181         }
40182         
40183         if(this.html.length){
40184             cn.push({
40185                 tag: 'p',
40186                 cls: 'masonry-brick-text',
40187                 html: this.html
40188             });
40189         }
40190         
40191         if (!this.title.length && !this.html.length) {
40192             cfg.cn[1].cls += ' hide';
40193         }
40194         
40195         if(this.bgimage.length){
40196             cfg.cn.push({
40197                 tag: 'img',
40198                 cls: 'masonry-brick-image-view',
40199                 src: this.bgimage
40200             });
40201         }
40202         
40203         if(this.videourl.length){
40204             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40205             // youtube support only?
40206             cfg.cn.push({
40207                 tag: 'iframe',
40208                 cls: 'masonry-brick-image-view',
40209                 src: vurl,
40210                 frameborder : 0,
40211                 allowfullscreen : true
40212             });
40213         }
40214         
40215         return cfg;
40216         
40217     },
40218     
40219     getSplitAutoCreate : function()
40220     {
40221         var cls = 'masonry-brick masonry-brick-split';
40222         
40223         if(this.href.length){
40224             cls += ' masonry-brick-link';
40225         }
40226         
40227         if(this.bgimage.length){
40228             cls += ' masonry-brick-image';
40229         }
40230         
40231         if(this.size){
40232             cls += ' masonry-' + this.size + '-brick';
40233         }
40234         
40235         switch (this.placetitle) {
40236             case 'center' :
40237                 cls += ' masonry-center-title';
40238                 break;
40239             case 'bottom' :
40240                 cls += ' masonry-bottom-title';
40241                 break;
40242             default:
40243                 if(!this.bgimage.length){
40244                     cls += ' masonry-center-title';
40245                 }
40246
40247                 if(this.bgimage.length){
40248                     cls += ' masonry-bottom-title';
40249                 }
40250                 break;
40251         }
40252         
40253         if(this.cls){
40254             cls += ' ' + this.cls;
40255         }
40256         
40257         var cfg = {
40258             tag: (this.href.length) ? 'a' : 'div',
40259             cls: cls,
40260             cn: [
40261                 {
40262                     tag: 'div',
40263                     cls: 'masonry-brick-split-head',
40264                     cn: [
40265                         {
40266                             tag: 'div',
40267                             cls: 'masonry-brick-paragraph',
40268                             cn: []
40269                         }
40270                     ]
40271                 },
40272                 {
40273                     tag: 'div',
40274                     cls: 'masonry-brick-split-body',
40275                     cn: []
40276                 }
40277             ]
40278         };
40279         
40280         if(this.href.length){
40281             cfg.href = this.href;
40282         }
40283         
40284         if(this.title.length){
40285             cfg.cn[0].cn[0].cn.push({
40286                 tag: 'h4',
40287                 cls: 'masonry-brick-title',
40288                 html: this.title
40289             });
40290         }
40291         
40292         if(this.html.length){
40293             cfg.cn[1].cn.push({
40294                 tag: 'p',
40295                 cls: 'masonry-brick-text',
40296                 html: this.html
40297             });
40298         }
40299
40300         if(this.bgimage.length){
40301             cfg.cn[0].cn.push({
40302                 tag: 'img',
40303                 cls: 'masonry-brick-image-view',
40304                 src: this.bgimage
40305             });
40306         }
40307         
40308         if(this.videourl.length){
40309             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40310             // youtube support only?
40311             cfg.cn[0].cn.cn.push({
40312                 tag: 'iframe',
40313                 cls: 'masonry-brick-image-view',
40314                 src: vurl,
40315                 frameborder : 0,
40316                 allowfullscreen : true
40317             });
40318         }
40319         
40320         return cfg;
40321     },
40322     
40323     initEvents: function() 
40324     {
40325         switch (this.size) {
40326             case 'xs' :
40327                 this.x = 1;
40328                 this.y = 1;
40329                 break;
40330             case 'sm' :
40331                 this.x = 2;
40332                 this.y = 2;
40333                 break;
40334             case 'md' :
40335             case 'md-left' :
40336             case 'md-right' :
40337                 this.x = 3;
40338                 this.y = 3;
40339                 break;
40340             case 'tall' :
40341                 this.x = 2;
40342                 this.y = 3;
40343                 break;
40344             case 'wide' :
40345                 this.x = 3;
40346                 this.y = 2;
40347                 break;
40348             case 'wide-thin' :
40349                 this.x = 3;
40350                 this.y = 1;
40351                 break;
40352                         
40353             default :
40354                 break;
40355         }
40356         
40357         if(Roo.isTouch){
40358             this.el.on('touchstart', this.onTouchStart, this);
40359             this.el.on('touchmove', this.onTouchMove, this);
40360             this.el.on('touchend', this.onTouchEnd, this);
40361             this.el.on('contextmenu', this.onContextMenu, this);
40362         } else {
40363             this.el.on('mouseenter'  ,this.enter, this);
40364             this.el.on('mouseleave', this.leave, this);
40365             this.el.on('click', this.onClick, this);
40366         }
40367         
40368         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40369             this.parent().bricks.push(this);   
40370         }
40371         
40372     },
40373     
40374     onClick: function(e, el)
40375     {
40376         var time = this.endTimer - this.startTimer;
40377         // Roo.log(e.preventDefault());
40378         if(Roo.isTouch){
40379             if(time > 1000){
40380                 e.preventDefault();
40381                 return;
40382             }
40383         }
40384         
40385         if(!this.preventDefault){
40386             return;
40387         }
40388         
40389         e.preventDefault();
40390         
40391         if (this.activeClass != '') {
40392             this.selectBrick();
40393         }
40394         
40395         this.fireEvent('click', this, e);
40396     },
40397     
40398     enter: function(e, el)
40399     {
40400         e.preventDefault();
40401         
40402         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40403             return;
40404         }
40405         
40406         if(this.bgimage.length && this.html.length){
40407             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40408         }
40409     },
40410     
40411     leave: function(e, el)
40412     {
40413         e.preventDefault();
40414         
40415         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40416             return;
40417         }
40418         
40419         if(this.bgimage.length && this.html.length){
40420             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40421         }
40422     },
40423     
40424     onTouchStart: function(e, el)
40425     {
40426 //        e.preventDefault();
40427         
40428         this.touchmoved = false;
40429         
40430         if(!this.isFitContainer){
40431             return;
40432         }
40433         
40434         if(!this.bgimage.length || !this.html.length){
40435             return;
40436         }
40437         
40438         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40439         
40440         this.timer = new Date().getTime();
40441         
40442     },
40443     
40444     onTouchMove: function(e, el)
40445     {
40446         this.touchmoved = true;
40447     },
40448     
40449     onContextMenu : function(e,el)
40450     {
40451         e.preventDefault();
40452         e.stopPropagation();
40453         return false;
40454     },
40455     
40456     onTouchEnd: function(e, el)
40457     {
40458 //        e.preventDefault();
40459         
40460         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40461         
40462             this.leave(e,el);
40463             
40464             return;
40465         }
40466         
40467         if(!this.bgimage.length || !this.html.length){
40468             
40469             if(this.href.length){
40470                 window.location.href = this.href;
40471             }
40472             
40473             return;
40474         }
40475         
40476         if(!this.isFitContainer){
40477             return;
40478         }
40479         
40480         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40481         
40482         window.location.href = this.href;
40483     },
40484     
40485     //selection on single brick only
40486     selectBrick : function() {
40487         
40488         if (!this.parentId) {
40489             return;
40490         }
40491         
40492         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40493         var index = m.selectedBrick.indexOf(this.id);
40494         
40495         if ( index > -1) {
40496             m.selectedBrick.splice(index,1);
40497             this.el.removeClass(this.activeClass);
40498             return;
40499         }
40500         
40501         for(var i = 0; i < m.selectedBrick.length; i++) {
40502             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40503             b.el.removeClass(b.activeClass);
40504         }
40505         
40506         m.selectedBrick = [];
40507         
40508         m.selectedBrick.push(this.id);
40509         this.el.addClass(this.activeClass);
40510         return;
40511     },
40512     
40513     isSelected : function(){
40514         return this.el.hasClass(this.activeClass);
40515         
40516     }
40517 });
40518
40519 Roo.apply(Roo.bootstrap.MasonryBrick, {
40520     
40521     //groups: {},
40522     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40523      /**
40524     * register a Masonry Brick
40525     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40526     */
40527     
40528     register : function(brick)
40529     {
40530         //this.groups[brick.id] = brick;
40531         this.groups.add(brick.id, brick);
40532     },
40533     /**
40534     * fetch a  masonry brick based on the masonry brick ID
40535     * @param {string} the masonry brick to add
40536     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
40537     */
40538     
40539     get: function(brick_id) 
40540     {
40541         // if (typeof(this.groups[brick_id]) == 'undefined') {
40542         //     return false;
40543         // }
40544         // return this.groups[brick_id] ;
40545         
40546         if(this.groups.key(brick_id)) {
40547             return this.groups.key(brick_id);
40548         }
40549         
40550         return false;
40551     }
40552     
40553     
40554     
40555 });
40556
40557  /*
40558  * - LGPL
40559  *
40560  * element
40561  * 
40562  */
40563
40564 /**
40565  * @class Roo.bootstrap.Brick
40566  * @extends Roo.bootstrap.Component
40567  * Bootstrap Brick class
40568  * 
40569  * @constructor
40570  * Create a new Brick
40571  * @param {Object} config The config object
40572  */
40573
40574 Roo.bootstrap.Brick = function(config){
40575     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
40576     
40577     this.addEvents({
40578         // raw events
40579         /**
40580          * @event click
40581          * When a Brick is click
40582          * @param {Roo.bootstrap.Brick} this
40583          * @param {Roo.EventObject} e
40584          */
40585         "click" : true
40586     });
40587 };
40588
40589 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
40590     
40591     /**
40592      * @cfg {String} title
40593      */   
40594     title : '',
40595     /**
40596      * @cfg {String} html
40597      */   
40598     html : '',
40599     /**
40600      * @cfg {String} bgimage
40601      */   
40602     bgimage : '',
40603     /**
40604      * @cfg {String} cls
40605      */   
40606     cls : '',
40607     /**
40608      * @cfg {String} href
40609      */   
40610     href : '',
40611     /**
40612      * @cfg {String} video
40613      */   
40614     video : '',
40615     /**
40616      * @cfg {Boolean} square
40617      */   
40618     square : true,
40619     
40620     getAutoCreate : function()
40621     {
40622         var cls = 'roo-brick';
40623         
40624         if(this.href.length){
40625             cls += ' roo-brick-link';
40626         }
40627         
40628         if(this.bgimage.length){
40629             cls += ' roo-brick-image';
40630         }
40631         
40632         if(!this.html.length && !this.bgimage.length){
40633             cls += ' roo-brick-center-title';
40634         }
40635         
40636         if(!this.html.length && this.bgimage.length){
40637             cls += ' roo-brick-bottom-title';
40638         }
40639         
40640         if(this.cls){
40641             cls += ' ' + this.cls;
40642         }
40643         
40644         var cfg = {
40645             tag: (this.href.length) ? 'a' : 'div',
40646             cls: cls,
40647             cn: [
40648                 {
40649                     tag: 'div',
40650                     cls: 'roo-brick-paragraph',
40651                     cn: []
40652                 }
40653             ]
40654         };
40655         
40656         if(this.href.length){
40657             cfg.href = this.href;
40658         }
40659         
40660         var cn = cfg.cn[0].cn;
40661         
40662         if(this.title.length){
40663             cn.push({
40664                 tag: 'h4',
40665                 cls: 'roo-brick-title',
40666                 html: this.title
40667             });
40668         }
40669         
40670         if(this.html.length){
40671             cn.push({
40672                 tag: 'p',
40673                 cls: 'roo-brick-text',
40674                 html: this.html
40675             });
40676         } else {
40677             cn.cls += ' hide';
40678         }
40679         
40680         if(this.bgimage.length){
40681             cfg.cn.push({
40682                 tag: 'img',
40683                 cls: 'roo-brick-image-view',
40684                 src: this.bgimage
40685             });
40686         }
40687         
40688         return cfg;
40689     },
40690     
40691     initEvents: function() 
40692     {
40693         if(this.title.length || this.html.length){
40694             this.el.on('mouseenter'  ,this.enter, this);
40695             this.el.on('mouseleave', this.leave, this);
40696         }
40697         
40698         Roo.EventManager.onWindowResize(this.resize, this); 
40699         
40700         if(this.bgimage.length){
40701             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
40702             this.imageEl.on('load', this.onImageLoad, this);
40703             return;
40704         }
40705         
40706         this.resize();
40707     },
40708     
40709     onImageLoad : function()
40710     {
40711         this.resize();
40712     },
40713     
40714     resize : function()
40715     {
40716         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
40717         
40718         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
40719         
40720         if(this.bgimage.length){
40721             var image = this.el.select('.roo-brick-image-view', true).first();
40722             
40723             image.setWidth(paragraph.getWidth());
40724             
40725             if(this.square){
40726                 image.setHeight(paragraph.getWidth());
40727             }
40728             
40729             this.el.setHeight(image.getHeight());
40730             paragraph.setHeight(image.getHeight());
40731             
40732         }
40733         
40734     },
40735     
40736     enter: function(e, el)
40737     {
40738         e.preventDefault();
40739         
40740         if(this.bgimage.length){
40741             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
40742             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
40743         }
40744     },
40745     
40746     leave: function(e, el)
40747     {
40748         e.preventDefault();
40749         
40750         if(this.bgimage.length){
40751             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
40752             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
40753         }
40754     }
40755     
40756 });
40757
40758  
40759
40760  /*
40761  * - LGPL
40762  *
40763  * Number field 
40764  */
40765
40766 /**
40767  * @class Roo.bootstrap.form.NumberField
40768  * @extends Roo.bootstrap.form.Input
40769  * Bootstrap NumberField class
40770  * 
40771  * 
40772  * 
40773  * 
40774  * @constructor
40775  * Create a new NumberField
40776  * @param {Object} config The config object
40777  */
40778
40779 Roo.bootstrap.form.NumberField = function(config){
40780     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
40781 };
40782
40783 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
40784     
40785     /**
40786      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40787      */
40788     allowDecimals : true,
40789     /**
40790      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40791      */
40792     decimalSeparator : ".",
40793     /**
40794      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40795      */
40796     decimalPrecision : 2,
40797     /**
40798      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40799      */
40800     allowNegative : true,
40801     
40802     /**
40803      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40804      */
40805     allowZero: true,
40806     /**
40807      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40808      */
40809     minValue : Number.NEGATIVE_INFINITY,
40810     /**
40811      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40812      */
40813     maxValue : Number.MAX_VALUE,
40814     /**
40815      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40816      */
40817     minText : "The minimum value for this field is {0}",
40818     /**
40819      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40820      */
40821     maxText : "The maximum value for this field is {0}",
40822     /**
40823      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40824      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40825      */
40826     nanText : "{0} is not a valid number",
40827     /**
40828      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40829      */
40830     thousandsDelimiter : false,
40831     /**
40832      * @cfg {String} valueAlign alignment of value
40833      */
40834     valueAlign : "left",
40835
40836     getAutoCreate : function()
40837     {
40838         var hiddenInput = {
40839             tag: 'input',
40840             type: 'hidden',
40841             id: Roo.id(),
40842             cls: 'hidden-number-input'
40843         };
40844         
40845         if (this.name) {
40846             hiddenInput.name = this.name;
40847         }
40848         
40849         this.name = '';
40850         
40851         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
40852         
40853         this.name = hiddenInput.name;
40854         
40855         if(cfg.cn.length > 0) {
40856             cfg.cn.push(hiddenInput);
40857         }
40858         
40859         return cfg;
40860     },
40861
40862     // private
40863     initEvents : function()
40864     {   
40865         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
40866         
40867         var allowed = "0123456789";
40868         
40869         if(this.allowDecimals){
40870             allowed += this.decimalSeparator;
40871         }
40872         
40873         if(this.allowNegative){
40874             allowed += "-";
40875         }
40876         
40877         if(this.thousandsDelimiter) {
40878             allowed += ",";
40879         }
40880         
40881         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40882         
40883         var keyPress = function(e){
40884             
40885             var k = e.getKey();
40886             
40887             var c = e.getCharCode();
40888             
40889             if(
40890                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40891                     allowed.indexOf(String.fromCharCode(c)) === -1
40892             ){
40893                 e.stopEvent();
40894                 return;
40895             }
40896             
40897             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40898                 return;
40899             }
40900             
40901             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40902                 e.stopEvent();
40903             }
40904         };
40905         
40906         this.el.on("keypress", keyPress, this);
40907     },
40908     
40909     validateValue : function(value)
40910     {
40911         
40912         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
40913             return false;
40914         }
40915         
40916         var num = this.parseValue(value);
40917         
40918         if(isNaN(num)){
40919             this.markInvalid(String.format(this.nanText, value));
40920             return false;
40921         }
40922         
40923         if(num < this.minValue){
40924             this.markInvalid(String.format(this.minText, this.minValue));
40925             return false;
40926         }
40927         
40928         if(num > this.maxValue){
40929             this.markInvalid(String.format(this.maxText, this.maxValue));
40930             return false;
40931         }
40932         
40933         return true;
40934     },
40935
40936     getValue : function()
40937     {
40938         var v = this.hiddenEl().getValue();
40939         
40940         return this.fixPrecision(this.parseValue(v));
40941     },
40942
40943     parseValue : function(value)
40944     {
40945         if(this.thousandsDelimiter) {
40946             value += "";
40947             r = new RegExp(",", "g");
40948             value = value.replace(r, "");
40949         }
40950         
40951         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40952         return isNaN(value) ? '' : value;
40953     },
40954
40955     fixPrecision : function(value)
40956     {
40957         if(this.thousandsDelimiter) {
40958             value += "";
40959             r = new RegExp(",", "g");
40960             value = value.replace(r, "");
40961         }
40962         
40963         var nan = isNaN(value);
40964         
40965         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40966             return nan ? '' : value;
40967         }
40968         return parseFloat(value).toFixed(this.decimalPrecision);
40969     },
40970
40971     setValue : function(v)
40972     {
40973         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40974         
40975         this.value = v;
40976         
40977         if(this.rendered){
40978             
40979             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40980             
40981             this.inputEl().dom.value = (v == '') ? '' :
40982                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40983             
40984             if(!this.allowZero && v === '0') {
40985                 this.hiddenEl().dom.value = '';
40986                 this.inputEl().dom.value = '';
40987             }
40988             
40989             this.validate();
40990         }
40991     },
40992
40993     decimalPrecisionFcn : function(v)
40994     {
40995         return Math.floor(v);
40996     },
40997
40998     beforeBlur : function()
40999     {
41000         var v = this.parseValue(this.getRawValue());
41001         
41002         if(v || v === 0 || v === ''){
41003             this.setValue(v);
41004         }
41005     },
41006     
41007     hiddenEl : function()
41008     {
41009         return this.el.select('input.hidden-number-input',true).first();
41010     }
41011     
41012 });
41013
41014  
41015
41016 /*
41017 * Licence: LGPL
41018 */
41019
41020 /**
41021  * @class Roo.bootstrap.DocumentSlider
41022  * @extends Roo.bootstrap.Component
41023  * Bootstrap DocumentSlider class
41024  * 
41025  * @constructor
41026  * Create a new DocumentViewer
41027  * @param {Object} config The config object
41028  */
41029
41030 Roo.bootstrap.DocumentSlider = function(config){
41031     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41032     
41033     this.files = [];
41034     
41035     this.addEvents({
41036         /**
41037          * @event initial
41038          * Fire after initEvent
41039          * @param {Roo.bootstrap.DocumentSlider} this
41040          */
41041         "initial" : true,
41042         /**
41043          * @event update
41044          * Fire after update
41045          * @param {Roo.bootstrap.DocumentSlider} this
41046          */
41047         "update" : true,
41048         /**
41049          * @event click
41050          * Fire after click
41051          * @param {Roo.bootstrap.DocumentSlider} this
41052          */
41053         "click" : true
41054     });
41055 };
41056
41057 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41058     
41059     files : false,
41060     
41061     indicator : 0,
41062     
41063     getAutoCreate : function()
41064     {
41065         var cfg = {
41066             tag : 'div',
41067             cls : 'roo-document-slider',
41068             cn : [
41069                 {
41070                     tag : 'div',
41071                     cls : 'roo-document-slider-header',
41072                     cn : [
41073                         {
41074                             tag : 'div',
41075                             cls : 'roo-document-slider-header-title'
41076                         }
41077                     ]
41078                 },
41079                 {
41080                     tag : 'div',
41081                     cls : 'roo-document-slider-body',
41082                     cn : [
41083                         {
41084                             tag : 'div',
41085                             cls : 'roo-document-slider-prev',
41086                             cn : [
41087                                 {
41088                                     tag : 'i',
41089                                     cls : 'fa fa-chevron-left'
41090                                 }
41091                             ]
41092                         },
41093                         {
41094                             tag : 'div',
41095                             cls : 'roo-document-slider-thumb',
41096                             cn : [
41097                                 {
41098                                     tag : 'img',
41099                                     cls : 'roo-document-slider-image'
41100                                 }
41101                             ]
41102                         },
41103                         {
41104                             tag : 'div',
41105                             cls : 'roo-document-slider-next',
41106                             cn : [
41107                                 {
41108                                     tag : 'i',
41109                                     cls : 'fa fa-chevron-right'
41110                                 }
41111                             ]
41112                         }
41113                     ]
41114                 }
41115             ]
41116         };
41117         
41118         return cfg;
41119     },
41120     
41121     initEvents : function()
41122     {
41123         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41124         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41125         
41126         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41127         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41128         
41129         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41130         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41131         
41132         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41133         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41134         
41135         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41136         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41137         
41138         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41139         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41140         
41141         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41142         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41143         
41144         this.thumbEl.on('click', this.onClick, this);
41145         
41146         this.prevIndicator.on('click', this.prev, this);
41147         
41148         this.nextIndicator.on('click', this.next, this);
41149         
41150     },
41151     
41152     initial : function()
41153     {
41154         if(this.files.length){
41155             this.indicator = 1;
41156             this.update()
41157         }
41158         
41159         this.fireEvent('initial', this);
41160     },
41161     
41162     update : function()
41163     {
41164         this.imageEl.attr('src', this.files[this.indicator - 1]);
41165         
41166         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41167         
41168         this.prevIndicator.show();
41169         
41170         if(this.indicator == 1){
41171             this.prevIndicator.hide();
41172         }
41173         
41174         this.nextIndicator.show();
41175         
41176         if(this.indicator == this.files.length){
41177             this.nextIndicator.hide();
41178         }
41179         
41180         this.thumbEl.scrollTo('top');
41181         
41182         this.fireEvent('update', this);
41183     },
41184     
41185     onClick : function(e)
41186     {
41187         e.preventDefault();
41188         
41189         this.fireEvent('click', this);
41190     },
41191     
41192     prev : function(e)
41193     {
41194         e.preventDefault();
41195         
41196         this.indicator = Math.max(1, this.indicator - 1);
41197         
41198         this.update();
41199     },
41200     
41201     next : function(e)
41202     {
41203         e.preventDefault();
41204         
41205         this.indicator = Math.min(this.files.length, this.indicator + 1);
41206         
41207         this.update();
41208     }
41209 });
41210 /*
41211  * - LGPL
41212  *
41213  * RadioSet
41214  *
41215  *
41216  */
41217
41218 /**
41219  * @class Roo.bootstrap.form.RadioSet
41220  * @extends Roo.bootstrap.form.Input
41221  * @children Roo.bootstrap.form.Radio
41222  * Bootstrap RadioSet class
41223  * @cfg {String} indicatorpos (left|right) default left
41224  * @cfg {Boolean} inline (true|false) inline the element (default true)
41225  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41226  * @constructor
41227  * Create a new RadioSet
41228  * @param {Object} config The config object
41229  */
41230
41231 Roo.bootstrap.form.RadioSet = function(config){
41232     
41233     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41234     
41235     this.radioes = [];
41236     
41237     Roo.bootstrap.form.RadioSet.register(this);
41238     
41239     this.addEvents({
41240         /**
41241         * @event check
41242         * Fires when the element is checked or unchecked.
41243         * @param {Roo.bootstrap.form.RadioSet} this This radio
41244         * @param {Roo.bootstrap.form.Radio} item The checked item
41245         */
41246        check : true,
41247        /**
41248         * @event click
41249         * Fires when the element is click.
41250         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41251         * @param {Roo.bootstrap.form.Radio} item The checked item
41252         * @param {Roo.EventObject} e The event object
41253         */
41254        click : true
41255     });
41256     
41257 };
41258
41259 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41260
41261     radioes : false,
41262     
41263     inline : true,
41264     
41265     weight : '',
41266     
41267     indicatorpos : 'left',
41268     
41269     getAutoCreate : function()
41270     {
41271         var label = {
41272             tag : 'label',
41273             cls : 'roo-radio-set-label',
41274             cn : [
41275                 {
41276                     tag : 'span',
41277                     html : this.fieldLabel
41278                 }
41279             ]
41280         };
41281         if (Roo.bootstrap.version == 3) {
41282             
41283             
41284             if(this.indicatorpos == 'left'){
41285                 label.cn.unshift({
41286                     tag : 'i',
41287                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41288                     tooltip : 'This field is required'
41289                 });
41290             } else {
41291                 label.cn.push({
41292                     tag : 'i',
41293                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41294                     tooltip : 'This field is required'
41295                 });
41296             }
41297         }
41298         var items = {
41299             tag : 'div',
41300             cls : 'roo-radio-set-items'
41301         };
41302         
41303         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41304         
41305         if (align === 'left' && this.fieldLabel.length) {
41306             
41307             items = {
41308                 cls : "roo-radio-set-right", 
41309                 cn: [
41310                     items
41311                 ]
41312             };
41313             
41314             if(this.labelWidth > 12){
41315                 label.style = "width: " + this.labelWidth + 'px';
41316             }
41317             
41318             if(this.labelWidth < 13 && this.labelmd == 0){
41319                 this.labelmd = this.labelWidth;
41320             }
41321             
41322             if(this.labellg > 0){
41323                 label.cls += ' col-lg-' + this.labellg;
41324                 items.cls += ' col-lg-' + (12 - this.labellg);
41325             }
41326             
41327             if(this.labelmd > 0){
41328                 label.cls += ' col-md-' + this.labelmd;
41329                 items.cls += ' col-md-' + (12 - this.labelmd);
41330             }
41331             
41332             if(this.labelsm > 0){
41333                 label.cls += ' col-sm-' + this.labelsm;
41334                 items.cls += ' col-sm-' + (12 - this.labelsm);
41335             }
41336             
41337             if(this.labelxs > 0){
41338                 label.cls += ' col-xs-' + this.labelxs;
41339                 items.cls += ' col-xs-' + (12 - this.labelxs);
41340             }
41341         }
41342         
41343         var cfg = {
41344             tag : 'div',
41345             cls : 'roo-radio-set',
41346             cn : [
41347                 {
41348                     tag : 'input',
41349                     cls : 'roo-radio-set-input',
41350                     type : 'hidden',
41351                     name : this.name,
41352                     value : this.value ? this.value :  ''
41353                 },
41354                 label,
41355                 items
41356             ]
41357         };
41358         
41359         if(this.weight.length){
41360             cfg.cls += ' roo-radio-' + this.weight;
41361         }
41362         
41363         if(this.inline) {
41364             cfg.cls += ' roo-radio-set-inline';
41365         }
41366         
41367         var settings=this;
41368         ['xs','sm','md','lg'].map(function(size){
41369             if (settings[size]) {
41370                 cfg.cls += ' col-' + size + '-' + settings[size];
41371             }
41372         });
41373         
41374         return cfg;
41375         
41376     },
41377
41378     initEvents : function()
41379     {
41380         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41381         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41382         
41383         if(!this.fieldLabel.length){
41384             this.labelEl.hide();
41385         }
41386         
41387         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41388         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41389         
41390         this.indicator = this.indicatorEl();
41391         
41392         if(this.indicator){
41393             this.indicator.addClass('invisible');
41394         }
41395         
41396         this.originalValue = this.getValue();
41397         
41398     },
41399     
41400     inputEl: function ()
41401     {
41402         return this.el.select('.roo-radio-set-input', true).first();
41403     },
41404     
41405     getChildContainer : function()
41406     {
41407         return this.itemsEl;
41408     },
41409     
41410     register : function(item)
41411     {
41412         this.radioes.push(item);
41413         
41414     },
41415     
41416     validate : function()
41417     {   
41418         if(this.getVisibilityEl().hasClass('hidden')){
41419             return true;
41420         }
41421         
41422         var valid = false;
41423         
41424         Roo.each(this.radioes, function(i){
41425             if(!i.checked){
41426                 return;
41427             }
41428             
41429             valid = true;
41430             return false;
41431         });
41432         
41433         if(this.allowBlank) {
41434             return true;
41435         }
41436         
41437         if(this.disabled || valid){
41438             this.markValid();
41439             return true;
41440         }
41441         
41442         this.markInvalid();
41443         return false;
41444         
41445     },
41446     
41447     markValid : function()
41448     {
41449         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41450             this.indicatorEl().removeClass('visible');
41451             this.indicatorEl().addClass('invisible');
41452         }
41453         
41454         
41455         if (Roo.bootstrap.version == 3) {
41456             this.el.removeClass([this.invalidClass, this.validClass]);
41457             this.el.addClass(this.validClass);
41458         } else {
41459             this.el.removeClass(['is-invalid','is-valid']);
41460             this.el.addClass(['is-valid']);
41461         }
41462         this.fireEvent('valid', this);
41463     },
41464     
41465     markInvalid : function(msg)
41466     {
41467         if(this.allowBlank || this.disabled){
41468             return;
41469         }
41470         
41471         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41472             this.indicatorEl().removeClass('invisible');
41473             this.indicatorEl().addClass('visible');
41474         }
41475         if (Roo.bootstrap.version == 3) {
41476             this.el.removeClass([this.invalidClass, this.validClass]);
41477             this.el.addClass(this.invalidClass);
41478         } else {
41479             this.el.removeClass(['is-invalid','is-valid']);
41480             this.el.addClass(['is-invalid']);
41481         }
41482         
41483         this.fireEvent('invalid', this, msg);
41484         
41485     },
41486     
41487     setValue : function(v, suppressEvent)
41488     {   
41489         if(this.value === v){
41490             return;
41491         }
41492         
41493         this.value = v;
41494         
41495         if(this.rendered){
41496             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41497         }
41498         
41499         Roo.each(this.radioes, function(i){
41500             i.checked = false;
41501             i.el.removeClass('checked');
41502         });
41503         
41504         Roo.each(this.radioes, function(i){
41505             
41506             if(i.value === v || i.value.toString() === v.toString()){
41507                 i.checked = true;
41508                 i.el.addClass('checked');
41509                 
41510                 if(suppressEvent !== true){
41511                     this.fireEvent('check', this, i);
41512                 }
41513                 
41514                 return false;
41515             }
41516             
41517         }, this);
41518         
41519         this.validate();
41520     },
41521     
41522     clearInvalid : function(){
41523         
41524         if(!this.el || this.preventMark){
41525             return;
41526         }
41527         
41528         this.el.removeClass([this.invalidClass]);
41529         
41530         this.fireEvent('valid', this);
41531     }
41532     
41533 });
41534
41535 Roo.apply(Roo.bootstrap.form.RadioSet, {
41536     
41537     groups: {},
41538     
41539     register : function(set)
41540     {
41541         this.groups[set.name] = set;
41542     },
41543     
41544     get: function(name) 
41545     {
41546         if (typeof(this.groups[name]) == 'undefined') {
41547             return false;
41548         }
41549         
41550         return this.groups[name] ;
41551     }
41552     
41553 });
41554 /*
41555  * Based on:
41556  * Ext JS Library 1.1.1
41557  * Copyright(c) 2006-2007, Ext JS, LLC.
41558  *
41559  * Originally Released Under LGPL - original licence link has changed is not relivant.
41560  *
41561  * Fork - LGPL
41562  * <script type="text/javascript">
41563  */
41564
41565
41566 /**
41567  * @class Roo.bootstrap.SplitBar
41568  * @extends Roo.util.Observable
41569  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
41570  * <br><br>
41571  * Usage:
41572  * <pre><code>
41573 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
41574                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
41575 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
41576 split.minSize = 100;
41577 split.maxSize = 600;
41578 split.animate = true;
41579 split.on('moved', splitterMoved);
41580 </code></pre>
41581  * @constructor
41582  * Create a new SplitBar
41583  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
41584  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
41585  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41586  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
41587                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
41588                         position of the SplitBar).
41589  */
41590 Roo.bootstrap.SplitBar = function(cfg){
41591     
41592     /** @private */
41593     
41594     //{
41595     //  dragElement : elm
41596     //  resizingElement: el,
41597         // optional..
41598     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
41599     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
41600         // existingProxy ???
41601     //}
41602     
41603     this.el = Roo.get(cfg.dragElement, true);
41604     this.el.dom.unselectable = "on";
41605     /** @private */
41606     this.resizingEl = Roo.get(cfg.resizingElement, true);
41607
41608     /**
41609      * @private
41610      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41611      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
41612      * @type Number
41613      */
41614     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
41615     
41616     /**
41617      * The minimum size of the resizing element. (Defaults to 0)
41618      * @type Number
41619      */
41620     this.minSize = 0;
41621     
41622     /**
41623      * The maximum size of the resizing element. (Defaults to 2000)
41624      * @type Number
41625      */
41626     this.maxSize = 2000;
41627     
41628     /**
41629      * Whether to animate the transition to the new size
41630      * @type Boolean
41631      */
41632     this.animate = false;
41633     
41634     /**
41635      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
41636      * @type Boolean
41637      */
41638     this.useShim = false;
41639     
41640     /** @private */
41641     this.shim = null;
41642     
41643     if(!cfg.existingProxy){
41644         /** @private */
41645         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
41646     }else{
41647         this.proxy = Roo.get(cfg.existingProxy).dom;
41648     }
41649     /** @private */
41650     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
41651     
41652     /** @private */
41653     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
41654     
41655     /** @private */
41656     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
41657     
41658     /** @private */
41659     this.dragSpecs = {};
41660     
41661     /**
41662      * @private The adapter to use to positon and resize elements
41663      */
41664     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41665     this.adapter.init(this);
41666     
41667     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41668         /** @private */
41669         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
41670         this.el.addClass("roo-splitbar-h");
41671     }else{
41672         /** @private */
41673         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
41674         this.el.addClass("roo-splitbar-v");
41675     }
41676     
41677     this.addEvents({
41678         /**
41679          * @event resize
41680          * Fires when the splitter is moved (alias for {@link #event-moved})
41681          * @param {Roo.bootstrap.SplitBar} this
41682          * @param {Number} newSize the new width or height
41683          */
41684         "resize" : true,
41685         /**
41686          * @event moved
41687          * Fires when the splitter is moved
41688          * @param {Roo.bootstrap.SplitBar} this
41689          * @param {Number} newSize the new width or height
41690          */
41691         "moved" : true,
41692         /**
41693          * @event beforeresize
41694          * Fires before the splitter is dragged
41695          * @param {Roo.bootstrap.SplitBar} this
41696          */
41697         "beforeresize" : true,
41698
41699         "beforeapply" : true
41700     });
41701
41702     Roo.util.Observable.call(this);
41703 };
41704
41705 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
41706     onStartProxyDrag : function(x, y){
41707         this.fireEvent("beforeresize", this);
41708         if(!this.overlay){
41709             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
41710             o.unselectable();
41711             o.enableDisplayMode("block");
41712             // all splitbars share the same overlay
41713             Roo.bootstrap.SplitBar.prototype.overlay = o;
41714         }
41715         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
41716         this.overlay.show();
41717         Roo.get(this.proxy).setDisplayed("block");
41718         var size = this.adapter.getElementSize(this);
41719         this.activeMinSize = this.getMinimumSize();;
41720         this.activeMaxSize = this.getMaximumSize();;
41721         var c1 = size - this.activeMinSize;
41722         var c2 = Math.max(this.activeMaxSize - size, 0);
41723         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41724             this.dd.resetConstraints();
41725             this.dd.setXConstraint(
41726                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
41727                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
41728             );
41729             this.dd.setYConstraint(0, 0);
41730         }else{
41731             this.dd.resetConstraints();
41732             this.dd.setXConstraint(0, 0);
41733             this.dd.setYConstraint(
41734                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
41735                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
41736             );
41737          }
41738         this.dragSpecs.startSize = size;
41739         this.dragSpecs.startPoint = [x, y];
41740         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
41741     },
41742     
41743     /** 
41744      * @private Called after the drag operation by the DDProxy
41745      */
41746     onEndProxyDrag : function(e){
41747         Roo.get(this.proxy).setDisplayed(false);
41748         var endPoint = Roo.lib.Event.getXY(e);
41749         if(this.overlay){
41750             this.overlay.hide();
41751         }
41752         var newSize;
41753         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41754             newSize = this.dragSpecs.startSize + 
41755                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
41756                     endPoint[0] - this.dragSpecs.startPoint[0] :
41757                     this.dragSpecs.startPoint[0] - endPoint[0]
41758                 );
41759         }else{
41760             newSize = this.dragSpecs.startSize + 
41761                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
41762                     endPoint[1] - this.dragSpecs.startPoint[1] :
41763                     this.dragSpecs.startPoint[1] - endPoint[1]
41764                 );
41765         }
41766         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
41767         if(newSize != this.dragSpecs.startSize){
41768             if(this.fireEvent('beforeapply', this, newSize) !== false){
41769                 this.adapter.setElementSize(this, newSize);
41770                 this.fireEvent("moved", this, newSize);
41771                 this.fireEvent("resize", this, newSize);
41772             }
41773         }
41774     },
41775     
41776     /**
41777      * Get the adapter this SplitBar uses
41778      * @return The adapter object
41779      */
41780     getAdapter : function(){
41781         return this.adapter;
41782     },
41783     
41784     /**
41785      * Set the adapter this SplitBar uses
41786      * @param {Object} adapter A SplitBar adapter object
41787      */
41788     setAdapter : function(adapter){
41789         this.adapter = adapter;
41790         this.adapter.init(this);
41791     },
41792     
41793     /**
41794      * Gets the minimum size for the resizing element
41795      * @return {Number} The minimum size
41796      */
41797     getMinimumSize : function(){
41798         return this.minSize;
41799     },
41800     
41801     /**
41802      * Sets the minimum size for the resizing element
41803      * @param {Number} minSize The minimum size
41804      */
41805     setMinimumSize : function(minSize){
41806         this.minSize = minSize;
41807     },
41808     
41809     /**
41810      * Gets the maximum size for the resizing element
41811      * @return {Number} The maximum size
41812      */
41813     getMaximumSize : function(){
41814         return this.maxSize;
41815     },
41816     
41817     /**
41818      * Sets the maximum size for the resizing element
41819      * @param {Number} maxSize The maximum size
41820      */
41821     setMaximumSize : function(maxSize){
41822         this.maxSize = maxSize;
41823     },
41824     
41825     /**
41826      * Sets the initialize size for the resizing element
41827      * @param {Number} size The initial size
41828      */
41829     setCurrentSize : function(size){
41830         var oldAnimate = this.animate;
41831         this.animate = false;
41832         this.adapter.setElementSize(this, size);
41833         this.animate = oldAnimate;
41834     },
41835     
41836     /**
41837      * Destroy this splitbar. 
41838      * @param {Boolean} removeEl True to remove the element
41839      */
41840     destroy : function(removeEl){
41841         if(this.shim){
41842             this.shim.remove();
41843         }
41844         this.dd.unreg();
41845         this.proxy.parentNode.removeChild(this.proxy);
41846         if(removeEl){
41847             this.el.remove();
41848         }
41849     }
41850 });
41851
41852 /**
41853  * @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.
41854  */
41855 Roo.bootstrap.SplitBar.createProxy = function(dir){
41856     var proxy = new Roo.Element(document.createElement("div"));
41857     proxy.unselectable();
41858     var cls = 'roo-splitbar-proxy';
41859     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
41860     document.body.appendChild(proxy.dom);
41861     return proxy.dom;
41862 };
41863
41864 /** 
41865  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
41866  * Default Adapter. It assumes the splitter and resizing element are not positioned
41867  * elements and only gets/sets the width of the element. Generally used for table based layouts.
41868  */
41869 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
41870 };
41871
41872 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
41873     // do nothing for now
41874     init : function(s){
41875     
41876     },
41877     /**
41878      * Called before drag operations to get the current size of the resizing element. 
41879      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41880      */
41881      getElementSize : function(s){
41882         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41883             return s.resizingEl.getWidth();
41884         }else{
41885             return s.resizingEl.getHeight();
41886         }
41887     },
41888     
41889     /**
41890      * Called after drag operations to set the size of the resizing element.
41891      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41892      * @param {Number} newSize The new size to set
41893      * @param {Function} onComplete A function to be invoked when resizing is complete
41894      */
41895     setElementSize : function(s, newSize, onComplete){
41896         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41897             if(!s.animate){
41898                 s.resizingEl.setWidth(newSize);
41899                 if(onComplete){
41900                     onComplete(s, newSize);
41901                 }
41902             }else{
41903                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
41904             }
41905         }else{
41906             
41907             if(!s.animate){
41908                 s.resizingEl.setHeight(newSize);
41909                 if(onComplete){
41910                     onComplete(s, newSize);
41911                 }
41912             }else{
41913                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
41914             }
41915         }
41916     }
41917 };
41918
41919 /** 
41920  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
41921  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
41922  * Adapter that  moves the splitter element to align with the resized sizing element. 
41923  * Used with an absolute positioned SplitBar.
41924  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
41925  * document.body, make sure you assign an id to the body element.
41926  */
41927 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
41928     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41929     this.container = Roo.get(container);
41930 };
41931
41932 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
41933     init : function(s){
41934         this.basic.init(s);
41935     },
41936     
41937     getElementSize : function(s){
41938         return this.basic.getElementSize(s);
41939     },
41940     
41941     setElementSize : function(s, newSize, onComplete){
41942         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
41943     },
41944     
41945     moveSplitter : function(s){
41946         var yes = Roo.bootstrap.SplitBar;
41947         switch(s.placement){
41948             case yes.LEFT:
41949                 s.el.setX(s.resizingEl.getRight());
41950                 break;
41951             case yes.RIGHT:
41952                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
41953                 break;
41954             case yes.TOP:
41955                 s.el.setY(s.resizingEl.getBottom());
41956                 break;
41957             case yes.BOTTOM:
41958                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
41959                 break;
41960         }
41961     }
41962 };
41963
41964 /**
41965  * Orientation constant - Create a vertical SplitBar
41966  * @static
41967  * @type Number
41968  */
41969 Roo.bootstrap.SplitBar.VERTICAL = 1;
41970
41971 /**
41972  * Orientation constant - Create a horizontal SplitBar
41973  * @static
41974  * @type Number
41975  */
41976 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
41977
41978 /**
41979  * Placement constant - The resizing element is to the left of the splitter element
41980  * @static
41981  * @type Number
41982  */
41983 Roo.bootstrap.SplitBar.LEFT = 1;
41984
41985 /**
41986  * Placement constant - The resizing element is to the right of the splitter element
41987  * @static
41988  * @type Number
41989  */
41990 Roo.bootstrap.SplitBar.RIGHT = 2;
41991
41992 /**
41993  * Placement constant - The resizing element is positioned above the splitter element
41994  * @static
41995  * @type Number
41996  */
41997 Roo.bootstrap.SplitBar.TOP = 3;
41998
41999 /**
42000  * Placement constant - The resizing element is positioned under splitter element
42001  * @static
42002  * @type Number
42003  */
42004 Roo.bootstrap.SplitBar.BOTTOM = 4;
42005 /*
42006  * Based on:
42007  * Ext JS Library 1.1.1
42008  * Copyright(c) 2006-2007, Ext JS, LLC.
42009  *
42010  * Originally Released Under LGPL - original licence link has changed is not relivant.
42011  *
42012  * Fork - LGPL
42013  * <script type="text/javascript">
42014  */
42015
42016 /**
42017  * @class Roo.bootstrap.layout.Manager
42018  * @extends Roo.bootstrap.Component
42019  * @abstract
42020  * Base class for layout managers.
42021  */
42022 Roo.bootstrap.layout.Manager = function(config)
42023 {
42024     this.monitorWindowResize = true; // do this before we apply configuration.
42025     
42026     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42027
42028
42029
42030
42031
42032     /** false to disable window resize monitoring @type Boolean */
42033     
42034     this.regions = {};
42035     this.addEvents({
42036         /**
42037          * @event layout
42038          * Fires when a layout is performed.
42039          * @param {Roo.LayoutManager} this
42040          */
42041         "layout" : true,
42042         /**
42043          * @event regionresized
42044          * Fires when the user resizes a region.
42045          * @param {Roo.LayoutRegion} region The resized region
42046          * @param {Number} newSize The new size (width for east/west, height for north/south)
42047          */
42048         "regionresized" : true,
42049         /**
42050          * @event regioncollapsed
42051          * Fires when a region is collapsed.
42052          * @param {Roo.LayoutRegion} region The collapsed region
42053          */
42054         "regioncollapsed" : true,
42055         /**
42056          * @event regionexpanded
42057          * Fires when a region is expanded.
42058          * @param {Roo.LayoutRegion} region The expanded region
42059          */
42060         "regionexpanded" : true
42061     });
42062     this.updating = false;
42063
42064     if (config.el) {
42065         this.el = Roo.get(config.el);
42066         this.initEvents();
42067     }
42068
42069 };
42070
42071 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42072
42073
42074     regions : null,
42075
42076     monitorWindowResize : true,
42077
42078
42079     updating : false,
42080
42081
42082     onRender : function(ct, position)
42083     {
42084         if(!this.el){
42085             this.el = Roo.get(ct);
42086             this.initEvents();
42087         }
42088         //this.fireEvent('render',this);
42089     },
42090
42091
42092     initEvents: function()
42093     {
42094
42095
42096         // ie scrollbar fix
42097         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42098             document.body.scroll = "no";
42099         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42100             this.el.position('relative');
42101         }
42102         this.id = this.el.id;
42103         this.el.addClass("roo-layout-container");
42104         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42105         if(this.el.dom != document.body ) {
42106             this.el.on('resize', this.layout,this);
42107             this.el.on('show', this.layout,this);
42108         }
42109
42110     },
42111
42112     /**
42113      * Returns true if this layout is currently being updated
42114      * @return {Boolean}
42115      */
42116     isUpdating : function(){
42117         return this.updating;
42118     },
42119
42120     /**
42121      * Suspend the LayoutManager from doing auto-layouts while
42122      * making multiple add or remove calls
42123      */
42124     beginUpdate : function(){
42125         this.updating = true;
42126     },
42127
42128     /**
42129      * Restore auto-layouts and optionally disable the manager from performing a layout
42130      * @param {Boolean} noLayout true to disable a layout update
42131      */
42132     endUpdate : function(noLayout){
42133         this.updating = false;
42134         if(!noLayout){
42135             this.layout();
42136         }
42137     },
42138
42139     layout: function(){
42140         // abstract...
42141     },
42142
42143     onRegionResized : function(region, newSize){
42144         this.fireEvent("regionresized", region, newSize);
42145         this.layout();
42146     },
42147
42148     onRegionCollapsed : function(region){
42149         this.fireEvent("regioncollapsed", region);
42150     },
42151
42152     onRegionExpanded : function(region){
42153         this.fireEvent("regionexpanded", region);
42154     },
42155
42156     /**
42157      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42158      * performs box-model adjustments.
42159      * @return {Object} The size as an object {width: (the width), height: (the height)}
42160      */
42161     getViewSize : function()
42162     {
42163         var size;
42164         if(this.el.dom != document.body){
42165             size = this.el.getSize();
42166         }else{
42167             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42168         }
42169         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42170         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42171         return size;
42172     },
42173
42174     /**
42175      * Returns the Element this layout is bound to.
42176      * @return {Roo.Element}
42177      */
42178     getEl : function(){
42179         return this.el;
42180     },
42181
42182     /**
42183      * Returns the specified region.
42184      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42185      * @return {Roo.LayoutRegion}
42186      */
42187     getRegion : function(target){
42188         return this.regions[target.toLowerCase()];
42189     },
42190
42191     onWindowResize : function(){
42192         if(this.monitorWindowResize){
42193             this.layout();
42194         }
42195     }
42196 });
42197 /*
42198  * Based on:
42199  * Ext JS Library 1.1.1
42200  * Copyright(c) 2006-2007, Ext JS, LLC.
42201  *
42202  * Originally Released Under LGPL - original licence link has changed is not relivant.
42203  *
42204  * Fork - LGPL
42205  * <script type="text/javascript">
42206  */
42207 /**
42208  * @class Roo.bootstrap.layout.Border
42209  * @extends Roo.bootstrap.layout.Manager
42210  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42211  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42212  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42213  * please see: examples/bootstrap/nested.html<br><br>
42214  
42215 <b>The container the layout is rendered into can be either the body element or any other element.
42216 If it is not the body element, the container needs to either be an absolute positioned element,
42217 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42218 the container size if it is not the body element.</b>
42219
42220 * @constructor
42221 * Create a new Border
42222 * @param {Object} config Configuration options
42223  */
42224 Roo.bootstrap.layout.Border = function(config){
42225     config = config || {};
42226     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42227     
42228     
42229     
42230     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42231         if(config[region]){
42232             config[region].region = region;
42233             this.addRegion(config[region]);
42234         }
42235     },this);
42236     
42237 };
42238
42239 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42240
42241 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42242     
42243         /**
42244          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42245          */
42246         /**
42247          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42248          */
42249         /**
42250          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42251          */
42252         /**
42253          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42254          */
42255         /**
42256          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42257          */
42258         
42259         
42260         
42261         
42262     parent : false, // this might point to a 'nest' or a ???
42263     
42264     /**
42265      * Creates and adds a new region if it doesn't already exist.
42266      * @param {String} target The target region key (north, south, east, west or center).
42267      * @param {Object} config The regions config object
42268      * @return {BorderLayoutRegion} The new region
42269      */
42270     addRegion : function(config)
42271     {
42272         if(!this.regions[config.region]){
42273             var r = this.factory(config);
42274             this.bindRegion(r);
42275         }
42276         return this.regions[config.region];
42277     },
42278
42279     // private (kinda)
42280     bindRegion : function(r){
42281         this.regions[r.config.region] = r;
42282         
42283         r.on("visibilitychange",    this.layout, this);
42284         r.on("paneladded",          this.layout, this);
42285         r.on("panelremoved",        this.layout, this);
42286         r.on("invalidated",         this.layout, this);
42287         r.on("resized",             this.onRegionResized, this);
42288         r.on("collapsed",           this.onRegionCollapsed, this);
42289         r.on("expanded",            this.onRegionExpanded, this);
42290     },
42291
42292     /**
42293      * Performs a layout update.
42294      */
42295     layout : function()
42296     {
42297         if(this.updating) {
42298             return;
42299         }
42300         
42301         // render all the rebions if they have not been done alreayd?
42302         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42303             if(this.regions[region] && !this.regions[region].bodyEl){
42304                 this.regions[region].onRender(this.el)
42305             }
42306         },this);
42307         
42308         var size = this.getViewSize();
42309         var w = size.width;
42310         var h = size.height;
42311         var centerW = w;
42312         var centerH = h;
42313         var centerY = 0;
42314         var centerX = 0;
42315         //var x = 0, y = 0;
42316
42317         var rs = this.regions;
42318         var north = rs["north"];
42319         var south = rs["south"]; 
42320         var west = rs["west"];
42321         var east = rs["east"];
42322         var center = rs["center"];
42323         //if(this.hideOnLayout){ // not supported anymore
42324             //c.el.setStyle("display", "none");
42325         //}
42326         if(north && north.isVisible()){
42327             var b = north.getBox();
42328             var m = north.getMargins();
42329             b.width = w - (m.left+m.right);
42330             b.x = m.left;
42331             b.y = m.top;
42332             centerY = b.height + b.y + m.bottom;
42333             centerH -= centerY;
42334             north.updateBox(this.safeBox(b));
42335         }
42336         if(south && south.isVisible()){
42337             var b = south.getBox();
42338             var m = south.getMargins();
42339             b.width = w - (m.left+m.right);
42340             b.x = m.left;
42341             var totalHeight = (b.height + m.top + m.bottom);
42342             b.y = h - totalHeight + m.top;
42343             centerH -= totalHeight;
42344             south.updateBox(this.safeBox(b));
42345         }
42346         if(west && west.isVisible()){
42347             var b = west.getBox();
42348             var m = west.getMargins();
42349             b.height = centerH - (m.top+m.bottom);
42350             b.x = m.left;
42351             b.y = centerY + m.top;
42352             var totalWidth = (b.width + m.left + m.right);
42353             centerX += totalWidth;
42354             centerW -= totalWidth;
42355             west.updateBox(this.safeBox(b));
42356         }
42357         if(east && east.isVisible()){
42358             var b = east.getBox();
42359             var m = east.getMargins();
42360             b.height = centerH - (m.top+m.bottom);
42361             var totalWidth = (b.width + m.left + m.right);
42362             b.x = w - totalWidth + m.left;
42363             b.y = centerY + m.top;
42364             centerW -= totalWidth;
42365             east.updateBox(this.safeBox(b));
42366         }
42367         if(center){
42368             var m = center.getMargins();
42369             var centerBox = {
42370                 x: centerX + m.left,
42371                 y: centerY + m.top,
42372                 width: centerW - (m.left+m.right),
42373                 height: centerH - (m.top+m.bottom)
42374             };
42375             //if(this.hideOnLayout){
42376                 //center.el.setStyle("display", "block");
42377             //}
42378             center.updateBox(this.safeBox(centerBox));
42379         }
42380         this.el.repaint();
42381         this.fireEvent("layout", this);
42382     },
42383
42384     // private
42385     safeBox : function(box){
42386         box.width = Math.max(0, box.width);
42387         box.height = Math.max(0, box.height);
42388         return box;
42389     },
42390
42391     /**
42392      * Adds a ContentPanel (or subclass) to this layout.
42393      * @param {String} target The target region key (north, south, east, west or center).
42394      * @param {Roo.ContentPanel} panel The panel to add
42395      * @return {Roo.ContentPanel} The added panel
42396      */
42397     add : function(target, panel){
42398          
42399         target = target.toLowerCase();
42400         return this.regions[target].add(panel);
42401     },
42402
42403     /**
42404      * Remove a ContentPanel (or subclass) to this layout.
42405      * @param {String} target The target region key (north, south, east, west or center).
42406      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42407      * @return {Roo.ContentPanel} The removed panel
42408      */
42409     remove : function(target, panel){
42410         target = target.toLowerCase();
42411         return this.regions[target].remove(panel);
42412     },
42413
42414     /**
42415      * Searches all regions for a panel with the specified id
42416      * @param {String} panelId
42417      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42418      */
42419     findPanel : function(panelId){
42420         var rs = this.regions;
42421         for(var target in rs){
42422             if(typeof rs[target] != "function"){
42423                 var p = rs[target].getPanel(panelId);
42424                 if(p){
42425                     return p;
42426                 }
42427             }
42428         }
42429         return null;
42430     },
42431
42432     /**
42433      * Searches all regions for a panel with the specified id and activates (shows) it.
42434      * @param {String/ContentPanel} panelId The panels id or the panel itself
42435      * @return {Roo.ContentPanel} The shown panel or null
42436      */
42437     showPanel : function(panelId) {
42438       var rs = this.regions;
42439       for(var target in rs){
42440          var r = rs[target];
42441          if(typeof r != "function"){
42442             if(r.hasPanel(panelId)){
42443                return r.showPanel(panelId);
42444             }
42445          }
42446       }
42447       return null;
42448    },
42449
42450    /**
42451      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42452      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42453      */
42454    /*
42455     restoreState : function(provider){
42456         if(!provider){
42457             provider = Roo.state.Manager;
42458         }
42459         var sm = new Roo.LayoutStateManager();
42460         sm.init(this, provider);
42461     },
42462 */
42463  
42464  
42465     /**
42466      * Adds a xtype elements to the layout.
42467      * <pre><code>
42468
42469 layout.addxtype({
42470        xtype : 'ContentPanel',
42471        region: 'west',
42472        items: [ .... ]
42473    }
42474 );
42475
42476 layout.addxtype({
42477         xtype : 'NestedLayoutPanel',
42478         region: 'west',
42479         layout: {
42480            center: { },
42481            west: { }   
42482         },
42483         items : [ ... list of content panels or nested layout panels.. ]
42484    }
42485 );
42486 </code></pre>
42487      * @param {Object} cfg Xtype definition of item to add.
42488      */
42489     addxtype : function(cfg)
42490     {
42491         // basically accepts a pannel...
42492         // can accept a layout region..!?!?
42493         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42494         
42495         
42496         // theory?  children can only be panels??
42497         
42498         //if (!cfg.xtype.match(/Panel$/)) {
42499         //    return false;
42500         //}
42501         var ret = false;
42502         
42503         if (typeof(cfg.region) == 'undefined') {
42504             Roo.log("Failed to add Panel, region was not set");
42505             Roo.log(cfg);
42506             return false;
42507         }
42508         var region = cfg.region;
42509         delete cfg.region;
42510         
42511           
42512         var xitems = [];
42513         if (cfg.items) {
42514             xitems = cfg.items;
42515             delete cfg.items;
42516         }
42517         var nb = false;
42518         
42519         if ( region == 'center') {
42520             Roo.log("Center: " + cfg.title);
42521         }
42522         
42523         
42524         switch(cfg.xtype) 
42525         {
42526             case 'Content':  // ContentPanel (el, cfg)
42527             case 'Scroll':  // ContentPanel (el, cfg)
42528             case 'View': 
42529                 cfg.autoCreate = cfg.autoCreate || true;
42530                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42531                 //} else {
42532                 //    var el = this.el.createChild();
42533                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42534                 //}
42535                 
42536                 this.add(region, ret);
42537                 break;
42538             
42539             /*
42540             case 'TreePanel': // our new panel!
42541                 cfg.el = this.el.createChild();
42542                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42543                 this.add(region, ret);
42544                 break;
42545             */
42546             
42547             case 'Nest': 
42548                 // create a new Layout (which is  a Border Layout...
42549                 
42550                 var clayout = cfg.layout;
42551                 clayout.el  = this.el.createChild();
42552                 clayout.items   = clayout.items  || [];
42553                 
42554                 delete cfg.layout;
42555                 
42556                 // replace this exitems with the clayout ones..
42557                 xitems = clayout.items;
42558                  
42559                 // force background off if it's in center...
42560                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42561                     cfg.background = false;
42562                 }
42563                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
42564                 
42565                 
42566                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42567                 //console.log('adding nested layout panel '  + cfg.toSource());
42568                 this.add(region, ret);
42569                 nb = {}; /// find first...
42570                 break;
42571             
42572             case 'Grid':
42573                 
42574                 // needs grid and region
42575                 
42576                 //var el = this.getRegion(region).el.createChild();
42577                 /*
42578                  *var el = this.el.createChild();
42579                 // create the grid first...
42580                 cfg.grid.container = el;
42581                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
42582                 */
42583                 
42584                 if (region == 'center' && this.active ) {
42585                     cfg.background = false;
42586                 }
42587                 
42588                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42589                 
42590                 this.add(region, ret);
42591                 /*
42592                 if (cfg.background) {
42593                     // render grid on panel activation (if panel background)
42594                     ret.on('activate', function(gp) {
42595                         if (!gp.grid.rendered) {
42596                     //        gp.grid.render(el);
42597                         }
42598                     });
42599                 } else {
42600                   //  cfg.grid.render(el);
42601                 }
42602                 */
42603                 break;
42604            
42605            
42606             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
42607                 // it was the old xcomponent building that caused this before.
42608                 // espeically if border is the top element in the tree.
42609                 ret = this;
42610                 break; 
42611                 
42612                     
42613                 
42614                 
42615                 
42616             default:
42617                 /*
42618                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
42619                     
42620                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42621                     this.add(region, ret);
42622                 } else {
42623                 */
42624                     Roo.log(cfg);
42625                     throw "Can not add '" + cfg.xtype + "' to Border";
42626                     return null;
42627              
42628                                 
42629              
42630         }
42631         this.beginUpdate();
42632         // add children..
42633         var region = '';
42634         var abn = {};
42635         Roo.each(xitems, function(i)  {
42636             region = nb && i.region ? i.region : false;
42637             
42638             var add = ret.addxtype(i);
42639            
42640             if (region) {
42641                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
42642                 if (!i.background) {
42643                     abn[region] = nb[region] ;
42644                 }
42645             }
42646             
42647         });
42648         this.endUpdate();
42649
42650         // make the last non-background panel active..
42651         //if (nb) { Roo.log(abn); }
42652         if (nb) {
42653             
42654             for(var r in abn) {
42655                 region = this.getRegion(r);
42656                 if (region) {
42657                     // tried using nb[r], but it does not work..
42658                      
42659                     region.showPanel(abn[r]);
42660                    
42661                 }
42662             }
42663         }
42664         return ret;
42665         
42666     },
42667     
42668     
42669 // private
42670     factory : function(cfg)
42671     {
42672         
42673         var validRegions = Roo.bootstrap.layout.Border.regions;
42674
42675         var target = cfg.region;
42676         cfg.mgr = this;
42677         
42678         var r = Roo.bootstrap.layout;
42679         Roo.log(target);
42680         switch(target){
42681             case "north":
42682                 return new r.North(cfg);
42683             case "south":
42684                 return new r.South(cfg);
42685             case "east":
42686                 return new r.East(cfg);
42687             case "west":
42688                 return new r.West(cfg);
42689             case "center":
42690                 return new r.Center(cfg);
42691         }
42692         throw 'Layout region "'+target+'" not supported.';
42693     }
42694     
42695     
42696 });
42697  /*
42698  * Based on:
42699  * Ext JS Library 1.1.1
42700  * Copyright(c) 2006-2007, Ext JS, LLC.
42701  *
42702  * Originally Released Under LGPL - original licence link has changed is not relivant.
42703  *
42704  * Fork - LGPL
42705  * <script type="text/javascript">
42706  */
42707  
42708 /**
42709  * @class Roo.bootstrap.layout.Basic
42710  * @extends Roo.util.Observable
42711  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42712  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42713  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42714  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
42715  * @cfg {string}   region  the region that it inhabits..
42716  * @cfg {bool}   skipConfig skip config?
42717  * 
42718
42719  */
42720 Roo.bootstrap.layout.Basic = function(config){
42721     
42722     this.mgr = config.mgr;
42723     
42724     this.position = config.region;
42725     
42726     var skipConfig = config.skipConfig;
42727     
42728     this.events = {
42729         /**
42730          * @scope Roo.BasicLayoutRegion
42731          */
42732         
42733         /**
42734          * @event beforeremove
42735          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42736          * @param {Roo.LayoutRegion} this
42737          * @param {Roo.ContentPanel} panel The panel
42738          * @param {Object} e The cancel event object
42739          */
42740         "beforeremove" : true,
42741         /**
42742          * @event invalidated
42743          * Fires when the layout for this region is changed.
42744          * @param {Roo.LayoutRegion} this
42745          */
42746         "invalidated" : true,
42747         /**
42748          * @event visibilitychange
42749          * Fires when this region is shown or hidden 
42750          * @param {Roo.LayoutRegion} this
42751          * @param {Boolean} visibility true or false
42752          */
42753         "visibilitychange" : true,
42754         /**
42755          * @event paneladded
42756          * Fires when a panel is added. 
42757          * @param {Roo.LayoutRegion} this
42758          * @param {Roo.ContentPanel} panel The panel
42759          */
42760         "paneladded" : true,
42761         /**
42762          * @event panelremoved
42763          * Fires when a panel is removed. 
42764          * @param {Roo.LayoutRegion} this
42765          * @param {Roo.ContentPanel} panel The panel
42766          */
42767         "panelremoved" : true,
42768         /**
42769          * @event beforecollapse
42770          * Fires when this region before collapse.
42771          * @param {Roo.LayoutRegion} this
42772          */
42773         "beforecollapse" : true,
42774         /**
42775          * @event collapsed
42776          * Fires when this region is collapsed.
42777          * @param {Roo.LayoutRegion} this
42778          */
42779         "collapsed" : true,
42780         /**
42781          * @event expanded
42782          * Fires when this region is expanded.
42783          * @param {Roo.LayoutRegion} this
42784          */
42785         "expanded" : true,
42786         /**
42787          * @event slideshow
42788          * Fires when this region is slid into view.
42789          * @param {Roo.LayoutRegion} this
42790          */
42791         "slideshow" : true,
42792         /**
42793          * @event slidehide
42794          * Fires when this region slides out of view. 
42795          * @param {Roo.LayoutRegion} this
42796          */
42797         "slidehide" : true,
42798         /**
42799          * @event panelactivated
42800          * Fires when a panel is activated. 
42801          * @param {Roo.LayoutRegion} this
42802          * @param {Roo.ContentPanel} panel The activated panel
42803          */
42804         "panelactivated" : true,
42805         /**
42806          * @event resized
42807          * Fires when the user resizes this region. 
42808          * @param {Roo.LayoutRegion} this
42809          * @param {Number} newSize The new size (width for east/west, height for north/south)
42810          */
42811         "resized" : true
42812     };
42813     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42814     this.panels = new Roo.util.MixedCollection();
42815     this.panels.getKey = this.getPanelId.createDelegate(this);
42816     this.box = null;
42817     this.activePanel = null;
42818     // ensure listeners are added...
42819     
42820     if (config.listeners || config.events) {
42821         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
42822             listeners : config.listeners || {},
42823             events : config.events || {}
42824         });
42825     }
42826     
42827     if(skipConfig !== true){
42828         this.applyConfig(config);
42829     }
42830 };
42831
42832 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
42833 {
42834     getPanelId : function(p){
42835         return p.getId();
42836     },
42837     
42838     applyConfig : function(config){
42839         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42840         this.config = config;
42841         
42842     },
42843     
42844     /**
42845      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42846      * the width, for horizontal (north, south) the height.
42847      * @param {Number} newSize The new width or height
42848      */
42849     resizeTo : function(newSize){
42850         var el = this.el ? this.el :
42851                  (this.activePanel ? this.activePanel.getEl() : null);
42852         if(el){
42853             switch(this.position){
42854                 case "east":
42855                 case "west":
42856                     el.setWidth(newSize);
42857                     this.fireEvent("resized", this, newSize);
42858                 break;
42859                 case "north":
42860                 case "south":
42861                     el.setHeight(newSize);
42862                     this.fireEvent("resized", this, newSize);
42863                 break;                
42864             }
42865         }
42866     },
42867     
42868     getBox : function(){
42869         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42870     },
42871     
42872     getMargins : function(){
42873         return this.margins;
42874     },
42875     
42876     updateBox : function(box){
42877         this.box = box;
42878         var el = this.activePanel.getEl();
42879         el.dom.style.left = box.x + "px";
42880         el.dom.style.top = box.y + "px";
42881         this.activePanel.setSize(box.width, box.height);
42882     },
42883     
42884     /**
42885      * Returns the container element for this region.
42886      * @return {Roo.Element}
42887      */
42888     getEl : function(){
42889         return this.activePanel;
42890     },
42891     
42892     /**
42893      * Returns true if this region is currently visible.
42894      * @return {Boolean}
42895      */
42896     isVisible : function(){
42897         return this.activePanel ? true : false;
42898     },
42899     
42900     setActivePanel : function(panel){
42901         panel = this.getPanel(panel);
42902         if(this.activePanel && this.activePanel != panel){
42903             this.activePanel.setActiveState(false);
42904             this.activePanel.getEl().setLeftTop(-10000,-10000);
42905         }
42906         this.activePanel = panel;
42907         panel.setActiveState(true);
42908         if(this.box){
42909             panel.setSize(this.box.width, this.box.height);
42910         }
42911         this.fireEvent("panelactivated", this, panel);
42912         this.fireEvent("invalidated");
42913     },
42914     
42915     /**
42916      * Show the specified panel.
42917      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42918      * @return {Roo.ContentPanel} The shown panel or null
42919      */
42920     showPanel : function(panel){
42921         panel = this.getPanel(panel);
42922         if(panel){
42923             this.setActivePanel(panel);
42924         }
42925         return panel;
42926     },
42927     
42928     /**
42929      * Get the active panel for this region.
42930      * @return {Roo.ContentPanel} The active panel or null
42931      */
42932     getActivePanel : function(){
42933         return this.activePanel;
42934     },
42935     
42936     /**
42937      * Add the passed ContentPanel(s)
42938      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42939      * @return {Roo.ContentPanel} The panel added (if only one was added)
42940      */
42941     add : function(panel){
42942         if(arguments.length > 1){
42943             for(var i = 0, len = arguments.length; i < len; i++) {
42944                 this.add(arguments[i]);
42945             }
42946             return null;
42947         }
42948         if(this.hasPanel(panel)){
42949             this.showPanel(panel);
42950             return panel;
42951         }
42952         var el = panel.getEl();
42953         if(el.dom.parentNode != this.mgr.el.dom){
42954             this.mgr.el.dom.appendChild(el.dom);
42955         }
42956         if(panel.setRegion){
42957             panel.setRegion(this);
42958         }
42959         this.panels.add(panel);
42960         el.setStyle("position", "absolute");
42961         if(!panel.background){
42962             this.setActivePanel(panel);
42963             if(this.config.initialSize && this.panels.getCount()==1){
42964                 this.resizeTo(this.config.initialSize);
42965             }
42966         }
42967         this.fireEvent("paneladded", this, panel);
42968         return panel;
42969     },
42970     
42971     /**
42972      * Returns true if the panel is in this region.
42973      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42974      * @return {Boolean}
42975      */
42976     hasPanel : function(panel){
42977         if(typeof panel == "object"){ // must be panel obj
42978             panel = panel.getId();
42979         }
42980         return this.getPanel(panel) ? true : false;
42981     },
42982     
42983     /**
42984      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42985      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42986      * @param {Boolean} preservePanel Overrides the config preservePanel option
42987      * @return {Roo.ContentPanel} The panel that was removed
42988      */
42989     remove : function(panel, preservePanel){
42990         panel = this.getPanel(panel);
42991         if(!panel){
42992             return null;
42993         }
42994         var e = {};
42995         this.fireEvent("beforeremove", this, panel, e);
42996         if(e.cancel === true){
42997             return null;
42998         }
42999         var panelId = panel.getId();
43000         this.panels.removeKey(panelId);
43001         return panel;
43002     },
43003     
43004     /**
43005      * Returns the panel specified or null if it's not in this region.
43006      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43007      * @return {Roo.ContentPanel}
43008      */
43009     getPanel : function(id){
43010         if(typeof id == "object"){ // must be panel obj
43011             return id;
43012         }
43013         return this.panels.get(id);
43014     },
43015     
43016     /**
43017      * Returns this regions position (north/south/east/west/center).
43018      * @return {String} 
43019      */
43020     getPosition: function(){
43021         return this.position;    
43022     }
43023 });/*
43024  * Based on:
43025  * Ext JS Library 1.1.1
43026  * Copyright(c) 2006-2007, Ext JS, LLC.
43027  *
43028  * Originally Released Under LGPL - original licence link has changed is not relivant.
43029  *
43030  * Fork - LGPL
43031  * <script type="text/javascript">
43032  */
43033  
43034 /**
43035  * @class Roo.bootstrap.layout.Region
43036  * @extends Roo.bootstrap.layout.Basic
43037  * This class represents a region in a layout manager.
43038  
43039  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43040  * @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})
43041  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43042  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43043  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43044  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43045  * @cfg {String}    title           The title for the region (overrides panel titles)
43046  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43047  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43048  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43049  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43050  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43051  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43052  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43053  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43054  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43055  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43056
43057  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43058  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43059  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43060  * @cfg {Number}    width           For East/West panels
43061  * @cfg {Number}    height          For North/South panels
43062  * @cfg {Boolean}   split           To show the splitter
43063  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43064  * 
43065  * @cfg {string}   cls             Extra CSS classes to add to region
43066  * 
43067  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43068  * @cfg {string}   region  the region that it inhabits..
43069  *
43070
43071  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43072  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43073
43074  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43075  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43076  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43077  */
43078 Roo.bootstrap.layout.Region = function(config)
43079 {
43080     this.applyConfig(config);
43081
43082     var mgr = config.mgr;
43083     var pos = config.region;
43084     config.skipConfig = true;
43085     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43086     
43087     if (mgr.el) {
43088         this.onRender(mgr.el);   
43089     }
43090      
43091     this.visible = true;
43092     this.collapsed = false;
43093     this.unrendered_panels = [];
43094 };
43095
43096 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43097
43098     position: '', // set by wrapper (eg. north/south etc..)
43099     unrendered_panels : null,  // unrendered panels.
43100     
43101     tabPosition : false,
43102     
43103     mgr: false, // points to 'Border'
43104     
43105     
43106     createBody : function(){
43107         /** This region's body element 
43108         * @type Roo.Element */
43109         this.bodyEl = this.el.createChild({
43110                 tag: "div",
43111                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43112         });
43113     },
43114
43115     onRender: function(ctr, pos)
43116     {
43117         var dh = Roo.DomHelper;
43118         /** This region's container element 
43119         * @type Roo.Element */
43120         this.el = dh.append(ctr.dom, {
43121                 tag: "div",
43122                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43123             }, true);
43124         /** This region's title element 
43125         * @type Roo.Element */
43126     
43127         this.titleEl = dh.append(this.el.dom,  {
43128                 tag: "div",
43129                 unselectable: "on",
43130                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43131                 children:[
43132                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43133                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43134                 ]
43135             }, true);
43136         
43137         this.titleEl.enableDisplayMode();
43138         /** This region's title text element 
43139         * @type HTMLElement */
43140         this.titleTextEl = this.titleEl.dom.firstChild;
43141         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43142         /*
43143         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43144         this.closeBtn.enableDisplayMode();
43145         this.closeBtn.on("click", this.closeClicked, this);
43146         this.closeBtn.hide();
43147     */
43148         this.createBody(this.config);
43149         if(this.config.hideWhenEmpty){
43150             this.hide();
43151             this.on("paneladded", this.validateVisibility, this);
43152             this.on("panelremoved", this.validateVisibility, this);
43153         }
43154         if(this.autoScroll){
43155             this.bodyEl.setStyle("overflow", "auto");
43156         }else{
43157             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43158         }
43159         //if(c.titlebar !== false){
43160             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43161                 this.titleEl.hide();
43162             }else{
43163                 this.titleEl.show();
43164                 if(this.config.title){
43165                     this.titleTextEl.innerHTML = this.config.title;
43166                 }
43167             }
43168         //}
43169         if(this.config.collapsed){
43170             this.collapse(true);
43171         }
43172         if(this.config.hidden){
43173             this.hide();
43174         }
43175         
43176         if (this.unrendered_panels && this.unrendered_panels.length) {
43177             for (var i =0;i< this.unrendered_panels.length; i++) {
43178                 this.add(this.unrendered_panels[i]);
43179             }
43180             this.unrendered_panels = null;
43181             
43182         }
43183         
43184     },
43185     
43186     applyConfig : function(c)
43187     {
43188         /*
43189          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43190             var dh = Roo.DomHelper;
43191             if(c.titlebar !== false){
43192                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43193                 this.collapseBtn.on("click", this.collapse, this);
43194                 this.collapseBtn.enableDisplayMode();
43195                 /*
43196                 if(c.showPin === true || this.showPin){
43197                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43198                     this.stickBtn.enableDisplayMode();
43199                     this.stickBtn.on("click", this.expand, this);
43200                     this.stickBtn.hide();
43201                 }
43202                 
43203             }
43204             */
43205             /** This region's collapsed element
43206             * @type Roo.Element */
43207             /*
43208              *
43209             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43210                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43211             ]}, true);
43212             
43213             if(c.floatable !== false){
43214                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43215                this.collapsedEl.on("click", this.collapseClick, this);
43216             }
43217
43218             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43219                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43220                    id: "message", unselectable: "on", style:{"float":"left"}});
43221                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43222              }
43223             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43224             this.expandBtn.on("click", this.expand, this);
43225             
43226         }
43227         
43228         if(this.collapseBtn){
43229             this.collapseBtn.setVisible(c.collapsible == true);
43230         }
43231         
43232         this.cmargins = c.cmargins || this.cmargins ||
43233                          (this.position == "west" || this.position == "east" ?
43234                              {top: 0, left: 2, right:2, bottom: 0} :
43235                              {top: 2, left: 0, right:0, bottom: 2});
43236         */
43237         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43238         
43239         
43240         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43241         
43242         this.autoScroll = c.autoScroll || false;
43243         
43244         
43245        
43246         
43247         this.duration = c.duration || .30;
43248         this.slideDuration = c.slideDuration || .45;
43249         this.config = c;
43250        
43251     },
43252     /**
43253      * Returns true if this region is currently visible.
43254      * @return {Boolean}
43255      */
43256     isVisible : function(){
43257         return this.visible;
43258     },
43259
43260     /**
43261      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43262      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43263      */
43264     //setCollapsedTitle : function(title){
43265     //    title = title || "&#160;";
43266      //   if(this.collapsedTitleTextEl){
43267       //      this.collapsedTitleTextEl.innerHTML = title;
43268        // }
43269     //},
43270
43271     getBox : function(){
43272         var b;
43273       //  if(!this.collapsed){
43274             b = this.el.getBox(false, true);
43275        // }else{
43276           //  b = this.collapsedEl.getBox(false, true);
43277         //}
43278         return b;
43279     },
43280
43281     getMargins : function(){
43282         return this.margins;
43283         //return this.collapsed ? this.cmargins : this.margins;
43284     },
43285 /*
43286     highlight : function(){
43287         this.el.addClass("x-layout-panel-dragover");
43288     },
43289
43290     unhighlight : function(){
43291         this.el.removeClass("x-layout-panel-dragover");
43292     },
43293 */
43294     updateBox : function(box)
43295     {
43296         if (!this.bodyEl) {
43297             return; // not rendered yet..
43298         }
43299         
43300         this.box = box;
43301         if(!this.collapsed){
43302             this.el.dom.style.left = box.x + "px";
43303             this.el.dom.style.top = box.y + "px";
43304             this.updateBody(box.width, box.height);
43305         }else{
43306             this.collapsedEl.dom.style.left = box.x + "px";
43307             this.collapsedEl.dom.style.top = box.y + "px";
43308             this.collapsedEl.setSize(box.width, box.height);
43309         }
43310         if(this.tabs){
43311             this.tabs.autoSizeTabs();
43312         }
43313     },
43314
43315     updateBody : function(w, h)
43316     {
43317         if(w !== null){
43318             this.el.setWidth(w);
43319             w -= this.el.getBorderWidth("rl");
43320             if(this.config.adjustments){
43321                 w += this.config.adjustments[0];
43322             }
43323         }
43324         if(h !== null && h > 0){
43325             this.el.setHeight(h);
43326             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43327             h -= this.el.getBorderWidth("tb");
43328             if(this.config.adjustments){
43329                 h += this.config.adjustments[1];
43330             }
43331             this.bodyEl.setHeight(h);
43332             if(this.tabs){
43333                 h = this.tabs.syncHeight(h);
43334             }
43335         }
43336         if(this.panelSize){
43337             w = w !== null ? w : this.panelSize.width;
43338             h = h !== null ? h : this.panelSize.height;
43339         }
43340         if(this.activePanel){
43341             var el = this.activePanel.getEl();
43342             w = w !== null ? w : el.getWidth();
43343             h = h !== null ? h : el.getHeight();
43344             this.panelSize = {width: w, height: h};
43345             this.activePanel.setSize(w, h);
43346         }
43347         if(Roo.isIE && this.tabs){
43348             this.tabs.el.repaint();
43349         }
43350     },
43351
43352     /**
43353      * Returns the container element for this region.
43354      * @return {Roo.Element}
43355      */
43356     getEl : function(){
43357         return this.el;
43358     },
43359
43360     /**
43361      * Hides this region.
43362      */
43363     hide : function(){
43364         //if(!this.collapsed){
43365             this.el.dom.style.left = "-2000px";
43366             this.el.hide();
43367         //}else{
43368          //   this.collapsedEl.dom.style.left = "-2000px";
43369          //   this.collapsedEl.hide();
43370        // }
43371         this.visible = false;
43372         this.fireEvent("visibilitychange", this, false);
43373     },
43374
43375     /**
43376      * Shows this region if it was previously hidden.
43377      */
43378     show : function(){
43379         //if(!this.collapsed){
43380             this.el.show();
43381         //}else{
43382         //    this.collapsedEl.show();
43383        // }
43384         this.visible = true;
43385         this.fireEvent("visibilitychange", this, true);
43386     },
43387 /*
43388     closeClicked : function(){
43389         if(this.activePanel){
43390             this.remove(this.activePanel);
43391         }
43392     },
43393
43394     collapseClick : function(e){
43395         if(this.isSlid){
43396            e.stopPropagation();
43397            this.slideIn();
43398         }else{
43399            e.stopPropagation();
43400            this.slideOut();
43401         }
43402     },
43403 */
43404     /**
43405      * Collapses this region.
43406      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43407      */
43408     /*
43409     collapse : function(skipAnim, skipCheck = false){
43410         if(this.collapsed) {
43411             return;
43412         }
43413         
43414         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43415             
43416             this.collapsed = true;
43417             if(this.split){
43418                 this.split.el.hide();
43419             }
43420             if(this.config.animate && skipAnim !== true){
43421                 this.fireEvent("invalidated", this);
43422                 this.animateCollapse();
43423             }else{
43424                 this.el.setLocation(-20000,-20000);
43425                 this.el.hide();
43426                 this.collapsedEl.show();
43427                 this.fireEvent("collapsed", this);
43428                 this.fireEvent("invalidated", this);
43429             }
43430         }
43431         
43432     },
43433 */
43434     animateCollapse : function(){
43435         // overridden
43436     },
43437
43438     /**
43439      * Expands this region if it was previously collapsed.
43440      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43441      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43442      */
43443     /*
43444     expand : function(e, skipAnim){
43445         if(e) {
43446             e.stopPropagation();
43447         }
43448         if(!this.collapsed || this.el.hasActiveFx()) {
43449             return;
43450         }
43451         if(this.isSlid){
43452             this.afterSlideIn();
43453             skipAnim = true;
43454         }
43455         this.collapsed = false;
43456         if(this.config.animate && skipAnim !== true){
43457             this.animateExpand();
43458         }else{
43459             this.el.show();
43460             if(this.split){
43461                 this.split.el.show();
43462             }
43463             this.collapsedEl.setLocation(-2000,-2000);
43464             this.collapsedEl.hide();
43465             this.fireEvent("invalidated", this);
43466             this.fireEvent("expanded", this);
43467         }
43468     },
43469 */
43470     animateExpand : function(){
43471         // overridden
43472     },
43473
43474     initTabs : function()
43475     {
43476         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43477         
43478         var ts = new Roo.bootstrap.panel.Tabs({
43479             el: this.bodyEl.dom,
43480             region : this,
43481             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
43482             disableTooltips: this.config.disableTabTips,
43483             toolbar : this.config.toolbar
43484         });
43485         
43486         if(this.config.hideTabs){
43487             ts.stripWrap.setDisplayed(false);
43488         }
43489         this.tabs = ts;
43490         ts.resizeTabs = this.config.resizeTabs === true;
43491         ts.minTabWidth = this.config.minTabWidth || 40;
43492         ts.maxTabWidth = this.config.maxTabWidth || 250;
43493         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43494         ts.monitorResize = false;
43495         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43496         ts.bodyEl.addClass('roo-layout-tabs-body');
43497         this.panels.each(this.initPanelAsTab, this);
43498     },
43499
43500     initPanelAsTab : function(panel){
43501         var ti = this.tabs.addTab(
43502             panel.getEl().id,
43503             panel.getTitle(),
43504             null,
43505             this.config.closeOnTab && panel.isClosable(),
43506             panel.tpl
43507         );
43508         if(panel.tabTip !== undefined){
43509             ti.setTooltip(panel.tabTip);
43510         }
43511         ti.on("activate", function(){
43512               this.setActivePanel(panel);
43513         }, this);
43514         
43515         if(this.config.closeOnTab){
43516             ti.on("beforeclose", function(t, e){
43517                 e.cancel = true;
43518                 this.remove(panel);
43519             }, this);
43520         }
43521         
43522         panel.tabItem = ti;
43523         
43524         return ti;
43525     },
43526
43527     updatePanelTitle : function(panel, title)
43528     {
43529         if(this.activePanel == panel){
43530             this.updateTitle(title);
43531         }
43532         if(this.tabs){
43533             var ti = this.tabs.getTab(panel.getEl().id);
43534             ti.setText(title);
43535             if(panel.tabTip !== undefined){
43536                 ti.setTooltip(panel.tabTip);
43537             }
43538         }
43539     },
43540
43541     updateTitle : function(title){
43542         if(this.titleTextEl && !this.config.title){
43543             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43544         }
43545     },
43546
43547     setActivePanel : function(panel)
43548     {
43549         panel = this.getPanel(panel);
43550         if(this.activePanel && this.activePanel != panel){
43551             if(this.activePanel.setActiveState(false) === false){
43552                 return;
43553             }
43554         }
43555         this.activePanel = panel;
43556         panel.setActiveState(true);
43557         if(this.panelSize){
43558             panel.setSize(this.panelSize.width, this.panelSize.height);
43559         }
43560         if(this.closeBtn){
43561             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43562         }
43563         this.updateTitle(panel.getTitle());
43564         if(this.tabs){
43565             this.fireEvent("invalidated", this);
43566         }
43567         this.fireEvent("panelactivated", this, panel);
43568     },
43569
43570     /**
43571      * Shows the specified panel.
43572      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43573      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43574      */
43575     showPanel : function(panel)
43576     {
43577         panel = this.getPanel(panel);
43578         if(panel){
43579             if(this.tabs){
43580                 var tab = this.tabs.getTab(panel.getEl().id);
43581                 if(tab.isHidden()){
43582                     this.tabs.unhideTab(tab.id);
43583                 }
43584                 tab.activate();
43585             }else{
43586                 this.setActivePanel(panel);
43587             }
43588         }
43589         return panel;
43590     },
43591
43592     /**
43593      * Get the active panel for this region.
43594      * @return {Roo.ContentPanel} The active panel or null
43595      */
43596     getActivePanel : function(){
43597         return this.activePanel;
43598     },
43599
43600     validateVisibility : function(){
43601         if(this.panels.getCount() < 1){
43602             this.updateTitle("&#160;");
43603             this.closeBtn.hide();
43604             this.hide();
43605         }else{
43606             if(!this.isVisible()){
43607                 this.show();
43608             }
43609         }
43610     },
43611
43612     /**
43613      * Adds the passed ContentPanel(s) to this region.
43614      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43615      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43616      */
43617     add : function(panel)
43618     {
43619         if(arguments.length > 1){
43620             for(var i = 0, len = arguments.length; i < len; i++) {
43621                 this.add(arguments[i]);
43622             }
43623             return null;
43624         }
43625         
43626         // if we have not been rendered yet, then we can not really do much of this..
43627         if (!this.bodyEl) {
43628             this.unrendered_panels.push(panel);
43629             return panel;
43630         }
43631         
43632         
43633         
43634         
43635         if(this.hasPanel(panel)){
43636             this.showPanel(panel);
43637             return panel;
43638         }
43639         panel.setRegion(this);
43640         this.panels.add(panel);
43641        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43642             // sinle panel - no tab...?? would it not be better to render it with the tabs,
43643             // and hide them... ???
43644             this.bodyEl.dom.appendChild(panel.getEl().dom);
43645             if(panel.background !== true){
43646                 this.setActivePanel(panel);
43647             }
43648             this.fireEvent("paneladded", this, panel);
43649             return panel;
43650         }
43651         */
43652         if(!this.tabs){
43653             this.initTabs();
43654         }else{
43655             this.initPanelAsTab(panel);
43656         }
43657         
43658         
43659         if(panel.background !== true){
43660             this.tabs.activate(panel.getEl().id);
43661         }
43662         this.fireEvent("paneladded", this, panel);
43663         return panel;
43664     },
43665
43666     /**
43667      * Hides the tab for the specified panel.
43668      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43669      */
43670     hidePanel : function(panel){
43671         if(this.tabs && (panel = this.getPanel(panel))){
43672             this.tabs.hideTab(panel.getEl().id);
43673         }
43674     },
43675
43676     /**
43677      * Unhides the tab for a previously hidden panel.
43678      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43679      */
43680     unhidePanel : function(panel){
43681         if(this.tabs && (panel = this.getPanel(panel))){
43682             this.tabs.unhideTab(panel.getEl().id);
43683         }
43684     },
43685
43686     clearPanels : function(){
43687         while(this.panels.getCount() > 0){
43688              this.remove(this.panels.first());
43689         }
43690     },
43691
43692     /**
43693      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43694      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43695      * @param {Boolean} preservePanel Overrides the config preservePanel option
43696      * @return {Roo.ContentPanel} The panel that was removed
43697      */
43698     remove : function(panel, preservePanel)
43699     {
43700         panel = this.getPanel(panel);
43701         if(!panel){
43702             return null;
43703         }
43704         var e = {};
43705         this.fireEvent("beforeremove", this, panel, e);
43706         if(e.cancel === true){
43707             return null;
43708         }
43709         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43710         var panelId = panel.getId();
43711         this.panels.removeKey(panelId);
43712         if(preservePanel){
43713             document.body.appendChild(panel.getEl().dom);
43714         }
43715         if(this.tabs){
43716             this.tabs.removeTab(panel.getEl().id);
43717         }else if (!preservePanel){
43718             this.bodyEl.dom.removeChild(panel.getEl().dom);
43719         }
43720         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43721             var p = this.panels.first();
43722             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43723             tempEl.appendChild(p.getEl().dom);
43724             this.bodyEl.update("");
43725             this.bodyEl.dom.appendChild(p.getEl().dom);
43726             tempEl = null;
43727             this.updateTitle(p.getTitle());
43728             this.tabs = null;
43729             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43730             this.setActivePanel(p);
43731         }
43732         panel.setRegion(null);
43733         if(this.activePanel == panel){
43734             this.activePanel = null;
43735         }
43736         if(this.config.autoDestroy !== false && preservePanel !== true){
43737             try{panel.destroy();}catch(e){}
43738         }
43739         this.fireEvent("panelremoved", this, panel);
43740         return panel;
43741     },
43742
43743     /**
43744      * Returns the TabPanel component used by this region
43745      * @return {Roo.TabPanel}
43746      */
43747     getTabs : function(){
43748         return this.tabs;
43749     },
43750
43751     createTool : function(parentEl, className){
43752         var btn = Roo.DomHelper.append(parentEl, {
43753             tag: "div",
43754             cls: "x-layout-tools-button",
43755             children: [ {
43756                 tag: "div",
43757                 cls: "roo-layout-tools-button-inner " + className,
43758                 html: "&#160;"
43759             }]
43760         }, true);
43761         btn.addClassOnOver("roo-layout-tools-button-over");
43762         return btn;
43763     }
43764 });/*
43765  * Based on:
43766  * Ext JS Library 1.1.1
43767  * Copyright(c) 2006-2007, Ext JS, LLC.
43768  *
43769  * Originally Released Under LGPL - original licence link has changed is not relivant.
43770  *
43771  * Fork - LGPL
43772  * <script type="text/javascript">
43773  */
43774  
43775
43776
43777 /**
43778  * @class Roo.SplitLayoutRegion
43779  * @extends Roo.LayoutRegion
43780  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43781  */
43782 Roo.bootstrap.layout.Split = function(config){
43783     this.cursor = config.cursor;
43784     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
43785 };
43786
43787 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
43788 {
43789     splitTip : "Drag to resize.",
43790     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43791     useSplitTips : false,
43792
43793     applyConfig : function(config){
43794         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
43795     },
43796     
43797     onRender : function(ctr,pos) {
43798         
43799         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
43800         if(!this.config.split){
43801             return;
43802         }
43803         if(!this.split){
43804             
43805             var splitEl = Roo.DomHelper.append(ctr.dom,  {
43806                             tag: "div",
43807                             id: this.el.id + "-split",
43808                             cls: "roo-layout-split roo-layout-split-"+this.position,
43809                             html: "&#160;"
43810             });
43811             /** The SplitBar for this region 
43812             * @type Roo.SplitBar */
43813             // does not exist yet...
43814             Roo.log([this.position, this.orientation]);
43815             
43816             this.split = new Roo.bootstrap.SplitBar({
43817                 dragElement : splitEl,
43818                 resizingElement: this.el,
43819                 orientation : this.orientation
43820             });
43821             
43822             this.split.on("moved", this.onSplitMove, this);
43823             this.split.useShim = this.config.useShim === true;
43824             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43825             if(this.useSplitTips){
43826                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43827             }
43828             //if(config.collapsible){
43829             //    this.split.el.on("dblclick", this.collapse,  this);
43830             //}
43831         }
43832         if(typeof this.config.minSize != "undefined"){
43833             this.split.minSize = this.config.minSize;
43834         }
43835         if(typeof this.config.maxSize != "undefined"){
43836             this.split.maxSize = this.config.maxSize;
43837         }
43838         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
43839             this.hideSplitter();
43840         }
43841         
43842     },
43843
43844     getHMaxSize : function(){
43845          var cmax = this.config.maxSize || 10000;
43846          var center = this.mgr.getRegion("center");
43847          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43848     },
43849
43850     getVMaxSize : function(){
43851          var cmax = this.config.maxSize || 10000;
43852          var center = this.mgr.getRegion("center");
43853          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43854     },
43855
43856     onSplitMove : function(split, newSize){
43857         this.fireEvent("resized", this, newSize);
43858     },
43859     
43860     /** 
43861      * Returns the {@link Roo.SplitBar} for this region.
43862      * @return {Roo.SplitBar}
43863      */
43864     getSplitBar : function(){
43865         return this.split;
43866     },
43867     
43868     hide : function(){
43869         this.hideSplitter();
43870         Roo.bootstrap.layout.Split.superclass.hide.call(this);
43871     },
43872
43873     hideSplitter : function(){
43874         if(this.split){
43875             this.split.el.setLocation(-2000,-2000);
43876             this.split.el.hide();
43877         }
43878     },
43879
43880     show : function(){
43881         if(this.split){
43882             this.split.el.show();
43883         }
43884         Roo.bootstrap.layout.Split.superclass.show.call(this);
43885     },
43886     
43887     beforeSlide: function(){
43888         if(Roo.isGecko){// firefox overflow auto bug workaround
43889             this.bodyEl.clip();
43890             if(this.tabs) {
43891                 this.tabs.bodyEl.clip();
43892             }
43893             if(this.activePanel){
43894                 this.activePanel.getEl().clip();
43895                 
43896                 if(this.activePanel.beforeSlide){
43897                     this.activePanel.beforeSlide();
43898                 }
43899             }
43900         }
43901     },
43902     
43903     afterSlide : function(){
43904         if(Roo.isGecko){// firefox overflow auto bug workaround
43905             this.bodyEl.unclip();
43906             if(this.tabs) {
43907                 this.tabs.bodyEl.unclip();
43908             }
43909             if(this.activePanel){
43910                 this.activePanel.getEl().unclip();
43911                 if(this.activePanel.afterSlide){
43912                     this.activePanel.afterSlide();
43913                 }
43914             }
43915         }
43916     },
43917
43918     initAutoHide : function(){
43919         if(this.autoHide !== false){
43920             if(!this.autoHideHd){
43921                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43922                 this.autoHideHd = {
43923                     "mouseout": function(e){
43924                         if(!e.within(this.el, true)){
43925                             st.delay(500);
43926                         }
43927                     },
43928                     "mouseover" : function(e){
43929                         st.cancel();
43930                     },
43931                     scope : this
43932                 };
43933             }
43934             this.el.on(this.autoHideHd);
43935         }
43936     },
43937
43938     clearAutoHide : function(){
43939         if(this.autoHide !== false){
43940             this.el.un("mouseout", this.autoHideHd.mouseout);
43941             this.el.un("mouseover", this.autoHideHd.mouseover);
43942         }
43943     },
43944
43945     clearMonitor : function(){
43946         Roo.get(document).un("click", this.slideInIf, this);
43947     },
43948
43949     // these names are backwards but not changed for compat
43950     slideOut : function(){
43951         if(this.isSlid || this.el.hasActiveFx()){
43952             return;
43953         }
43954         this.isSlid = true;
43955         if(this.collapseBtn){
43956             this.collapseBtn.hide();
43957         }
43958         this.closeBtnState = this.closeBtn.getStyle('display');
43959         this.closeBtn.hide();
43960         if(this.stickBtn){
43961             this.stickBtn.show();
43962         }
43963         this.el.show();
43964         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43965         this.beforeSlide();
43966         this.el.setStyle("z-index", 10001);
43967         this.el.slideIn(this.getSlideAnchor(), {
43968             callback: function(){
43969                 this.afterSlide();
43970                 this.initAutoHide();
43971                 Roo.get(document).on("click", this.slideInIf, this);
43972                 this.fireEvent("slideshow", this);
43973             },
43974             scope: this,
43975             block: true
43976         });
43977     },
43978
43979     afterSlideIn : function(){
43980         this.clearAutoHide();
43981         this.isSlid = false;
43982         this.clearMonitor();
43983         this.el.setStyle("z-index", "");
43984         if(this.collapseBtn){
43985             this.collapseBtn.show();
43986         }
43987         this.closeBtn.setStyle('display', this.closeBtnState);
43988         if(this.stickBtn){
43989             this.stickBtn.hide();
43990         }
43991         this.fireEvent("slidehide", this);
43992     },
43993
43994     slideIn : function(cb){
43995         if(!this.isSlid || this.el.hasActiveFx()){
43996             Roo.callback(cb);
43997             return;
43998         }
43999         this.isSlid = false;
44000         this.beforeSlide();
44001         this.el.slideOut(this.getSlideAnchor(), {
44002             callback: function(){
44003                 this.el.setLeftTop(-10000, -10000);
44004                 this.afterSlide();
44005                 this.afterSlideIn();
44006                 Roo.callback(cb);
44007             },
44008             scope: this,
44009             block: true
44010         });
44011     },
44012     
44013     slideInIf : function(e){
44014         if(!e.within(this.el)){
44015             this.slideIn();
44016         }
44017     },
44018
44019     animateCollapse : function(){
44020         this.beforeSlide();
44021         this.el.setStyle("z-index", 20000);
44022         var anchor = this.getSlideAnchor();
44023         this.el.slideOut(anchor, {
44024             callback : function(){
44025                 this.el.setStyle("z-index", "");
44026                 this.collapsedEl.slideIn(anchor, {duration:.3});
44027                 this.afterSlide();
44028                 this.el.setLocation(-10000,-10000);
44029                 this.el.hide();
44030                 this.fireEvent("collapsed", this);
44031             },
44032             scope: this,
44033             block: true
44034         });
44035     },
44036
44037     animateExpand : function(){
44038         this.beforeSlide();
44039         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44040         this.el.setStyle("z-index", 20000);
44041         this.collapsedEl.hide({
44042             duration:.1
44043         });
44044         this.el.slideIn(this.getSlideAnchor(), {
44045             callback : function(){
44046                 this.el.setStyle("z-index", "");
44047                 this.afterSlide();
44048                 if(this.split){
44049                     this.split.el.show();
44050                 }
44051                 this.fireEvent("invalidated", this);
44052                 this.fireEvent("expanded", this);
44053             },
44054             scope: this,
44055             block: true
44056         });
44057     },
44058
44059     anchors : {
44060         "west" : "left",
44061         "east" : "right",
44062         "north" : "top",
44063         "south" : "bottom"
44064     },
44065
44066     sanchors : {
44067         "west" : "l",
44068         "east" : "r",
44069         "north" : "t",
44070         "south" : "b"
44071     },
44072
44073     canchors : {
44074         "west" : "tl-tr",
44075         "east" : "tr-tl",
44076         "north" : "tl-bl",
44077         "south" : "bl-tl"
44078     },
44079
44080     getAnchor : function(){
44081         return this.anchors[this.position];
44082     },
44083
44084     getCollapseAnchor : function(){
44085         return this.canchors[this.position];
44086     },
44087
44088     getSlideAnchor : function(){
44089         return this.sanchors[this.position];
44090     },
44091
44092     getAlignAdj : function(){
44093         var cm = this.cmargins;
44094         switch(this.position){
44095             case "west":
44096                 return [0, 0];
44097             break;
44098             case "east":
44099                 return [0, 0];
44100             break;
44101             case "north":
44102                 return [0, 0];
44103             break;
44104             case "south":
44105                 return [0, 0];
44106             break;
44107         }
44108     },
44109
44110     getExpandAdj : function(){
44111         var c = this.collapsedEl, cm = this.cmargins;
44112         switch(this.position){
44113             case "west":
44114                 return [-(cm.right+c.getWidth()+cm.left), 0];
44115             break;
44116             case "east":
44117                 return [cm.right+c.getWidth()+cm.left, 0];
44118             break;
44119             case "north":
44120                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44121             break;
44122             case "south":
44123                 return [0, cm.top+cm.bottom+c.getHeight()];
44124             break;
44125         }
44126     }
44127 });/*
44128  * Based on:
44129  * Ext JS Library 1.1.1
44130  * Copyright(c) 2006-2007, Ext JS, LLC.
44131  *
44132  * Originally Released Under LGPL - original licence link has changed is not relivant.
44133  *
44134  * Fork - LGPL
44135  * <script type="text/javascript">
44136  */
44137 /*
44138  * These classes are private internal classes
44139  */
44140 Roo.bootstrap.layout.Center = function(config){
44141     config.region = "center";
44142     Roo.bootstrap.layout.Region.call(this, config);
44143     this.visible = true;
44144     this.minWidth = config.minWidth || 20;
44145     this.minHeight = config.minHeight || 20;
44146 };
44147
44148 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44149     hide : function(){
44150         // center panel can't be hidden
44151     },
44152     
44153     show : function(){
44154         // center panel can't be hidden
44155     },
44156     
44157     getMinWidth: function(){
44158         return this.minWidth;
44159     },
44160     
44161     getMinHeight: function(){
44162         return this.minHeight;
44163     }
44164 });
44165
44166
44167
44168
44169  
44170
44171
44172
44173
44174
44175
44176 Roo.bootstrap.layout.North = function(config)
44177 {
44178     config.region = 'north';
44179     config.cursor = 'n-resize';
44180     
44181     Roo.bootstrap.layout.Split.call(this, config);
44182     
44183     
44184     if(this.split){
44185         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44186         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44187         this.split.el.addClass("roo-layout-split-v");
44188     }
44189     //var size = config.initialSize || config.height;
44190     //if(this.el && typeof size != "undefined"){
44191     //    this.el.setHeight(size);
44192     //}
44193 };
44194 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44195 {
44196     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44197      
44198      
44199     onRender : function(ctr, pos)
44200     {
44201         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44202         var size = this.config.initialSize || this.config.height;
44203         if(this.el && typeof size != "undefined"){
44204             this.el.setHeight(size);
44205         }
44206     
44207     },
44208     
44209     getBox : function(){
44210         if(this.collapsed){
44211             return this.collapsedEl.getBox();
44212         }
44213         var box = this.el.getBox();
44214         if(this.split){
44215             box.height += this.split.el.getHeight();
44216         }
44217         return box;
44218     },
44219     
44220     updateBox : function(box){
44221         if(this.split && !this.collapsed){
44222             box.height -= this.split.el.getHeight();
44223             this.split.el.setLeft(box.x);
44224             this.split.el.setTop(box.y+box.height);
44225             this.split.el.setWidth(box.width);
44226         }
44227         if(this.collapsed){
44228             this.updateBody(box.width, null);
44229         }
44230         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44231     }
44232 });
44233
44234
44235
44236
44237
44238 Roo.bootstrap.layout.South = function(config){
44239     config.region = 'south';
44240     config.cursor = 's-resize';
44241     Roo.bootstrap.layout.Split.call(this, config);
44242     if(this.split){
44243         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44244         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44245         this.split.el.addClass("roo-layout-split-v");
44246     }
44247     
44248 };
44249
44250 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44251     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44252     
44253     onRender : function(ctr, pos)
44254     {
44255         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44256         var size = this.config.initialSize || this.config.height;
44257         if(this.el && typeof size != "undefined"){
44258             this.el.setHeight(size);
44259         }
44260     
44261     },
44262     
44263     getBox : function(){
44264         if(this.collapsed){
44265             return this.collapsedEl.getBox();
44266         }
44267         var box = this.el.getBox();
44268         if(this.split){
44269             var sh = this.split.el.getHeight();
44270             box.height += sh;
44271             box.y -= sh;
44272         }
44273         return box;
44274     },
44275     
44276     updateBox : function(box){
44277         if(this.split && !this.collapsed){
44278             var sh = this.split.el.getHeight();
44279             box.height -= sh;
44280             box.y += sh;
44281             this.split.el.setLeft(box.x);
44282             this.split.el.setTop(box.y-sh);
44283             this.split.el.setWidth(box.width);
44284         }
44285         if(this.collapsed){
44286             this.updateBody(box.width, null);
44287         }
44288         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44289     }
44290 });
44291
44292 Roo.bootstrap.layout.East = function(config){
44293     config.region = "east";
44294     config.cursor = "e-resize";
44295     Roo.bootstrap.layout.Split.call(this, config);
44296     if(this.split){
44297         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44298         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44299         this.split.el.addClass("roo-layout-split-h");
44300     }
44301     
44302 };
44303 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44304     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44305     
44306     onRender : function(ctr, pos)
44307     {
44308         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44309         var size = this.config.initialSize || this.config.width;
44310         if(this.el && typeof size != "undefined"){
44311             this.el.setWidth(size);
44312         }
44313     
44314     },
44315     
44316     getBox : function(){
44317         if(this.collapsed){
44318             return this.collapsedEl.getBox();
44319         }
44320         var box = this.el.getBox();
44321         if(this.split){
44322             var sw = this.split.el.getWidth();
44323             box.width += sw;
44324             box.x -= sw;
44325         }
44326         return box;
44327     },
44328
44329     updateBox : function(box){
44330         if(this.split && !this.collapsed){
44331             var sw = this.split.el.getWidth();
44332             box.width -= sw;
44333             this.split.el.setLeft(box.x);
44334             this.split.el.setTop(box.y);
44335             this.split.el.setHeight(box.height);
44336             box.x += sw;
44337         }
44338         if(this.collapsed){
44339             this.updateBody(null, box.height);
44340         }
44341         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44342     }
44343 });
44344
44345 Roo.bootstrap.layout.West = function(config){
44346     config.region = "west";
44347     config.cursor = "w-resize";
44348     
44349     Roo.bootstrap.layout.Split.call(this, config);
44350     if(this.split){
44351         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44352         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44353         this.split.el.addClass("roo-layout-split-h");
44354     }
44355     
44356 };
44357 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44358     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44359     
44360     onRender: function(ctr, pos)
44361     {
44362         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44363         var size = this.config.initialSize || this.config.width;
44364         if(typeof size != "undefined"){
44365             this.el.setWidth(size);
44366         }
44367     },
44368     
44369     getBox : function(){
44370         if(this.collapsed){
44371             return this.collapsedEl.getBox();
44372         }
44373         var box = this.el.getBox();
44374         if (box.width == 0) {
44375             box.width = this.config.width; // kludge?
44376         }
44377         if(this.split){
44378             box.width += this.split.el.getWidth();
44379         }
44380         return box;
44381     },
44382     
44383     updateBox : function(box){
44384         if(this.split && !this.collapsed){
44385             var sw = this.split.el.getWidth();
44386             box.width -= sw;
44387             this.split.el.setLeft(box.x+box.width);
44388             this.split.el.setTop(box.y);
44389             this.split.el.setHeight(box.height);
44390         }
44391         if(this.collapsed){
44392             this.updateBody(null, box.height);
44393         }
44394         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44395     }
44396 });/*
44397  * Based on:
44398  * Ext JS Library 1.1.1
44399  * Copyright(c) 2006-2007, Ext JS, LLC.
44400  *
44401  * Originally Released Under LGPL - original licence link has changed is not relivant.
44402  *
44403  * Fork - LGPL
44404  * <script type="text/javascript">
44405  */
44406 /**
44407  * @class Roo.bootstrap.paenl.Content
44408  * @extends Roo.util.Observable
44409  * @children Roo.bootstrap.Component
44410  * @parent builder Roo.bootstrap.layout.Border
44411  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44412  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44413  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44414  * @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
44415  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44416  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44417  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44418  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44419  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44420  * @cfg {String} title          The title for this panel
44421  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44422  * @cfg {String} url            Calls {@link #setUrl} with this value
44423  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44424  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44425  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44426  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44427  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44428  * @cfg {Boolean} badges render the badges
44429  * @cfg {String} cls  extra classes to use  
44430  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44431  
44432  * @constructor
44433  * Create a new ContentPanel.
44434  * @param {String/Object} config A string to set only the title or a config object
44435  
44436  */
44437 Roo.bootstrap.panel.Content = function( config){
44438     
44439     this.tpl = config.tpl || false;
44440     
44441     var el = config.el;
44442     var content = config.content;
44443
44444     if(config.autoCreate){ // xtype is available if this is called from factory
44445         el = Roo.id();
44446     }
44447     this.el = Roo.get(el);
44448     if(!this.el && config && config.autoCreate){
44449         if(typeof config.autoCreate == "object"){
44450             if(!config.autoCreate.id){
44451                 config.autoCreate.id = config.id||el;
44452             }
44453             this.el = Roo.DomHelper.append(document.body,
44454                         config.autoCreate, true);
44455         }else{
44456             var elcfg =  {
44457                 tag: "div",
44458                 cls: (config.cls || '') +
44459                     (config.background ? ' bg-' + config.background : '') +
44460                     " roo-layout-inactive-content",
44461                 id: config.id||el
44462             };
44463             if (config.iframe) {
44464                 elcfg.cn = [
44465                     {
44466                         tag : 'iframe',
44467                         style : 'border: 0px',
44468                         src : 'about:blank'
44469                     }
44470                 ];
44471             }
44472               
44473             if (config.html) {
44474                 elcfg.html = config.html;
44475                 
44476             }
44477                         
44478             this.el = Roo.DomHelper.append(document.body, elcfg , true);
44479             if (config.iframe) {
44480                 this.iframeEl = this.el.select('iframe',true).first();
44481             }
44482             
44483         }
44484     } 
44485     this.closable = false;
44486     this.loaded = false;
44487     this.active = false;
44488    
44489       
44490     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44491         
44492         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44493         
44494         this.wrapEl = this.el; //this.el.wrap();
44495         var ti = [];
44496         if (config.toolbar.items) {
44497             ti = config.toolbar.items ;
44498             delete config.toolbar.items ;
44499         }
44500         
44501         var nitems = [];
44502         this.toolbar.render(this.wrapEl, 'before');
44503         for(var i =0;i < ti.length;i++) {
44504           //  Roo.log(['add child', items[i]]);
44505             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44506         }
44507         this.toolbar.items = nitems;
44508         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44509         delete config.toolbar;
44510         
44511     }
44512     /*
44513     // xtype created footer. - not sure if will work as we normally have to render first..
44514     if (this.footer && !this.footer.el && this.footer.xtype) {
44515         if (!this.wrapEl) {
44516             this.wrapEl = this.el.wrap();
44517         }
44518     
44519         this.footer.container = this.wrapEl.createChild();
44520          
44521         this.footer = Roo.factory(this.footer, Roo);
44522         
44523     }
44524     */
44525     
44526      if(typeof config == "string"){
44527         this.title = config;
44528     }else{
44529         Roo.apply(this, config);
44530     }
44531     
44532     if(this.resizeEl){
44533         this.resizeEl = Roo.get(this.resizeEl, true);
44534     }else{
44535         this.resizeEl = this.el;
44536     }
44537     // handle view.xtype
44538     
44539  
44540     
44541     
44542     this.addEvents({
44543         /**
44544          * @event activate
44545          * Fires when this panel is activated. 
44546          * @param {Roo.ContentPanel} this
44547          */
44548         "activate" : true,
44549         /**
44550          * @event deactivate
44551          * Fires when this panel is activated. 
44552          * @param {Roo.ContentPanel} this
44553          */
44554         "deactivate" : true,
44555
44556         /**
44557          * @event resize
44558          * Fires when this panel is resized if fitToFrame is true.
44559          * @param {Roo.ContentPanel} this
44560          * @param {Number} width The width after any component adjustments
44561          * @param {Number} height The height after any component adjustments
44562          */
44563         "resize" : true,
44564         
44565          /**
44566          * @event render
44567          * Fires when this tab is created
44568          * @param {Roo.ContentPanel} this
44569          */
44570         "render" : true,
44571         
44572           /**
44573          * @event scroll
44574          * Fires when this content is scrolled
44575          * @param {Roo.ContentPanel} this
44576          * @param {Event} scrollEvent
44577          */
44578         "scroll" : true
44579         
44580         
44581         
44582     });
44583     
44584
44585     
44586     
44587     if(this.autoScroll && !this.iframe){
44588         this.resizeEl.setStyle("overflow", "auto");
44589         this.resizeEl.on('scroll', this.onScroll, this);
44590     } else {
44591         // fix randome scrolling
44592         //this.el.on('scroll', function() {
44593         //    Roo.log('fix random scolling');
44594         //    this.scrollTo('top',0); 
44595         //});
44596     }
44597     content = content || this.content;
44598     if(content){
44599         this.setContent(content);
44600     }
44601     if(config && config.url){
44602         this.setUrl(this.url, this.params, this.loadOnce);
44603     }
44604     
44605     
44606     
44607     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
44608     
44609     if (this.view && typeof(this.view.xtype) != 'undefined') {
44610         this.view.el = this.el.appendChild(document.createElement("div"));
44611         this.view = Roo.factory(this.view); 
44612         this.view.render  &&  this.view.render(false, '');  
44613     }
44614     
44615     
44616     this.fireEvent('render', this);
44617 };
44618
44619 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
44620     
44621     cls : '',
44622     background : '',
44623     
44624     tabTip : '',
44625     
44626     iframe : false,
44627     iframeEl : false,
44628     
44629     /* Resize Element - use this to work out scroll etc. */
44630     resizeEl : false,
44631     
44632     setRegion : function(region){
44633         this.region = region;
44634         this.setActiveClass(region && !this.background);
44635     },
44636     
44637     
44638     setActiveClass: function(state)
44639     {
44640         if(state){
44641            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
44642            this.el.setStyle('position','relative');
44643         }else{
44644            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
44645            this.el.setStyle('position', 'absolute');
44646         } 
44647     },
44648     
44649     /**
44650      * Returns the toolbar for this Panel if one was configured. 
44651      * @return {Roo.Toolbar} 
44652      */
44653     getToolbar : function(){
44654         return this.toolbar;
44655     },
44656     
44657     setActiveState : function(active)
44658     {
44659         this.active = active;
44660         this.setActiveClass(active);
44661         if(!active){
44662             if(this.fireEvent("deactivate", this) === false){
44663                 return false;
44664             }
44665             return true;
44666         }
44667         this.fireEvent("activate", this);
44668         return true;
44669     },
44670     /**
44671      * Updates this panel's element (not for iframe)
44672      * @param {String} content The new content
44673      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44674     */
44675     setContent : function(content, loadScripts){
44676         if (this.iframe) {
44677             return;
44678         }
44679         
44680         this.el.update(content, loadScripts);
44681     },
44682
44683     ignoreResize : function(w, h)
44684     {
44685         //return false; // always resize?
44686         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44687             return true;
44688         }else{
44689             this.lastSize = {width: w, height: h};
44690             return false;
44691         }
44692     },
44693     /**
44694      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44695      * @return {Roo.UpdateManager} The UpdateManager
44696      */
44697     getUpdateManager : function(){
44698         if (this.iframe) {
44699             return false;
44700         }
44701         return this.el.getUpdateManager();
44702     },
44703      /**
44704      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44705      * Does not work with IFRAME contents
44706      * @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:
44707 <pre><code>
44708 panel.load({
44709     url: "your-url.php",
44710     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44711     callback: yourFunction,
44712     scope: yourObject, //(optional scope)
44713     discardUrl: false,
44714     nocache: false,
44715     text: "Loading...",
44716     timeout: 30,
44717     scripts: false
44718 });
44719 </code></pre>
44720      
44721      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44722      * 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.
44723      * @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}
44724      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44725      * @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.
44726      * @return {Roo.ContentPanel} this
44727      */
44728     load : function(){
44729         
44730         if (this.iframe) {
44731             return this;
44732         }
44733         
44734         var um = this.el.getUpdateManager();
44735         um.update.apply(um, arguments);
44736         return this;
44737     },
44738
44739
44740     /**
44741      * 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.
44742      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44743      * @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)
44744      * @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)
44745      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
44746      */
44747     setUrl : function(url, params, loadOnce){
44748         if (this.iframe) {
44749             this.iframeEl.dom.src = url;
44750             return false;
44751         }
44752         
44753         if(this.refreshDelegate){
44754             this.removeListener("activate", this.refreshDelegate);
44755         }
44756         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44757         this.on("activate", this.refreshDelegate);
44758         return this.el.getUpdateManager();
44759     },
44760     
44761     _handleRefresh : function(url, params, loadOnce){
44762         if(!loadOnce || !this.loaded){
44763             var updater = this.el.getUpdateManager();
44764             updater.update(url, params, this._setLoaded.createDelegate(this));
44765         }
44766     },
44767     
44768     _setLoaded : function(){
44769         this.loaded = true;
44770     }, 
44771     
44772     /**
44773      * Returns this panel's id
44774      * @return {String} 
44775      */
44776     getId : function(){
44777         return this.el.id;
44778     },
44779     
44780     /** 
44781      * Returns this panel's element - used by regiosn to add.
44782      * @return {Roo.Element} 
44783      */
44784     getEl : function(){
44785         return this.wrapEl || this.el;
44786     },
44787     
44788    
44789     
44790     adjustForComponents : function(width, height)
44791     {
44792         //Roo.log('adjustForComponents ');
44793         if(this.resizeEl != this.el){
44794             width -= this.el.getFrameWidth('lr');
44795             height -= this.el.getFrameWidth('tb');
44796         }
44797         if(this.toolbar){
44798             var te = this.toolbar.getEl();
44799             te.setWidth(width);
44800             height -= te.getHeight();
44801         }
44802         if(this.footer){
44803             var te = this.footer.getEl();
44804             te.setWidth(width);
44805             height -= te.getHeight();
44806         }
44807         
44808         
44809         if(this.adjustments){
44810             width += this.adjustments[0];
44811             height += this.adjustments[1];
44812         }
44813         return {"width": width, "height": height};
44814     },
44815     
44816     setSize : function(width, height){
44817         if(this.fitToFrame && !this.ignoreResize(width, height)){
44818             if(this.fitContainer && this.resizeEl != this.el){
44819                 this.el.setSize(width, height);
44820             }
44821             var size = this.adjustForComponents(width, height);
44822             if (this.iframe) {
44823                 this.iframeEl.setSize(width,height);
44824             }
44825             
44826             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44827             this.fireEvent('resize', this, size.width, size.height);
44828             
44829             
44830         }
44831     },
44832     
44833     /**
44834      * Returns this panel's title
44835      * @return {String} 
44836      */
44837     getTitle : function(){
44838         
44839         if (typeof(this.title) != 'object') {
44840             return this.title;
44841         }
44842         
44843         var t = '';
44844         for (var k in this.title) {
44845             if (!this.title.hasOwnProperty(k)) {
44846                 continue;
44847             }
44848             
44849             if (k.indexOf('-') >= 0) {
44850                 var s = k.split('-');
44851                 for (var i = 0; i<s.length; i++) {
44852                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
44853                 }
44854             } else {
44855                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
44856             }
44857         }
44858         return t;
44859     },
44860     
44861     /**
44862      * Set this panel's title
44863      * @param {String} title
44864      */
44865     setTitle : function(title){
44866         this.title = title;
44867         if(this.region){
44868             this.region.updatePanelTitle(this, title);
44869         }
44870     },
44871     
44872     /**
44873      * Returns true is this panel was configured to be closable
44874      * @return {Boolean} 
44875      */
44876     isClosable : function(){
44877         return this.closable;
44878     },
44879     
44880     beforeSlide : function(){
44881         this.el.clip();
44882         this.resizeEl.clip();
44883     },
44884     
44885     afterSlide : function(){
44886         this.el.unclip();
44887         this.resizeEl.unclip();
44888     },
44889     
44890     /**
44891      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44892      *   Will fail silently if the {@link #setUrl} method has not been called.
44893      *   This does not activate the panel, just updates its content.
44894      */
44895     refresh : function(){
44896         if(this.refreshDelegate){
44897            this.loaded = false;
44898            this.refreshDelegate();
44899         }
44900     },
44901     
44902     /**
44903      * Destroys this panel
44904      */
44905     destroy : function(){
44906         this.el.removeAllListeners();
44907         var tempEl = document.createElement("span");
44908         tempEl.appendChild(this.el.dom);
44909         tempEl.innerHTML = "";
44910         this.el.remove();
44911         this.el = null;
44912     },
44913     
44914     /**
44915      * form - if the content panel contains a form - this is a reference to it.
44916      * @type {Roo.form.Form}
44917      */
44918     form : false,
44919     /**
44920      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
44921      *    This contains a reference to it.
44922      * @type {Roo.View}
44923      */
44924     view : false,
44925     
44926       /**
44927      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44928      * <pre><code>
44929
44930 layout.addxtype({
44931        xtype : 'Form',
44932        items: [ .... ]
44933    }
44934 );
44935
44936 </code></pre>
44937      * @param {Object} cfg Xtype definition of item to add.
44938      */
44939     
44940     
44941     getChildContainer: function () {
44942         return this.getEl();
44943     },
44944     
44945     
44946     onScroll : function(e)
44947     {
44948         this.fireEvent('scroll', this, e);
44949     }
44950     
44951     
44952     /*
44953         var  ret = new Roo.factory(cfg);
44954         return ret;
44955         
44956         
44957         // add form..
44958         if (cfg.xtype.match(/^Form$/)) {
44959             
44960             var el;
44961             //if (this.footer) {
44962             //    el = this.footer.container.insertSibling(false, 'before');
44963             //} else {
44964                 el = this.el.createChild();
44965             //}
44966
44967             this.form = new  Roo.form.Form(cfg);
44968             
44969             
44970             if ( this.form.allItems.length) {
44971                 this.form.render(el.dom);
44972             }
44973             return this.form;
44974         }
44975         // should only have one of theses..
44976         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
44977             // views.. should not be just added - used named prop 'view''
44978             
44979             cfg.el = this.el.appendChild(document.createElement("div"));
44980             // factory?
44981             
44982             var ret = new Roo.factory(cfg);
44983              
44984              ret.render && ret.render(false, ''); // render blank..
44985             this.view = ret;
44986             return ret;
44987         }
44988         return false;
44989     }
44990     \*/
44991 });
44992  
44993 /**
44994  * @class Roo.bootstrap.panel.Grid
44995  * @extends Roo.bootstrap.panel.Content
44996  * @constructor
44997  * Create a new GridPanel.
44998  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
44999  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45000  * @param {Object} config A the config object
45001   
45002  */
45003
45004
45005
45006 Roo.bootstrap.panel.Grid = function(config)
45007 {
45008     
45009       
45010     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45011         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45012
45013     config.el = this.wrapper;
45014     //this.el = this.wrapper;
45015     
45016       if (config.container) {
45017         // ctor'ed from a Border/panel.grid
45018         
45019         
45020         this.wrapper.setStyle("overflow", "hidden");
45021         this.wrapper.addClass('roo-grid-container');
45022
45023     }
45024     
45025     
45026     if(config.toolbar){
45027         var tool_el = this.wrapper.createChild();    
45028         this.toolbar = Roo.factory(config.toolbar);
45029         var ti = [];
45030         if (config.toolbar.items) {
45031             ti = config.toolbar.items ;
45032             delete config.toolbar.items ;
45033         }
45034         
45035         var nitems = [];
45036         this.toolbar.render(tool_el);
45037         for(var i =0;i < ti.length;i++) {
45038           //  Roo.log(['add child', items[i]]);
45039             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45040         }
45041         this.toolbar.items = nitems;
45042         
45043         delete config.toolbar;
45044     }
45045     
45046     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45047     config.grid.scrollBody = true;;
45048     config.grid.monitorWindowResize = false; // turn off autosizing
45049     config.grid.autoHeight = false;
45050     config.grid.autoWidth = false;
45051     
45052     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45053     
45054     if (config.background) {
45055         // render grid on panel activation (if panel background)
45056         this.on('activate', function(gp) {
45057             if (!gp.grid.rendered) {
45058                 gp.grid.render(this.wrapper);
45059                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45060             }
45061         });
45062             
45063     } else {
45064         this.grid.render(this.wrapper);
45065         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45066
45067     }
45068     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45069     // ??? needed ??? config.el = this.wrapper;
45070     
45071     
45072     
45073   
45074     // xtype created footer. - not sure if will work as we normally have to render first..
45075     if (this.footer && !this.footer.el && this.footer.xtype) {
45076         
45077         var ctr = this.grid.getView().getFooterPanel(true);
45078         this.footer.dataSource = this.grid.dataSource;
45079         this.footer = Roo.factory(this.footer, Roo);
45080         this.footer.render(ctr);
45081         
45082     }
45083     
45084     
45085     
45086     
45087      
45088 };
45089
45090 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45091 {
45092   
45093     getId : function(){
45094         return this.grid.id;
45095     },
45096     
45097     /**
45098      * Returns the grid for this panel
45099      * @return {Roo.bootstrap.Table} 
45100      */
45101     getGrid : function(){
45102         return this.grid;    
45103     },
45104     
45105     setSize : function(width, height)
45106     {
45107      
45108         //if(!this.ignoreResize(width, height)){
45109             var grid = this.grid;
45110             var size = this.adjustForComponents(width, height);
45111             // tfoot is not a footer?
45112           
45113             
45114             var gridel = grid.getGridEl();
45115             gridel.setSize(size.width, size.height);
45116             
45117             var tbd = grid.getGridEl().select('tbody', true).first();
45118             var thd = grid.getGridEl().select('thead',true).first();
45119             var tbf= grid.getGridEl().select('tfoot', true).first();
45120
45121             if (tbf) {
45122                 size.height -= tbf.getHeight();
45123             }
45124             if (thd) {
45125                 size.height -= thd.getHeight();
45126             }
45127             
45128             tbd.setSize(size.width, size.height );
45129             // this is for the account management tab -seems to work there.
45130             var thd = grid.getGridEl().select('thead',true).first();
45131             //if (tbd) {
45132             //    tbd.setSize(size.width, size.height - thd.getHeight());
45133             //}
45134              
45135             grid.autoSize();
45136         //}
45137    
45138     },
45139      
45140     
45141     
45142     beforeSlide : function(){
45143         this.grid.getView().scroller.clip();
45144     },
45145     
45146     afterSlide : function(){
45147         this.grid.getView().scroller.unclip();
45148     },
45149     
45150     destroy : function(){
45151         this.grid.destroy();
45152         delete this.grid;
45153         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45154     }
45155 });
45156
45157 /**
45158  * @class Roo.bootstrap.panel.Nest
45159  * @extends Roo.bootstrap.panel.Content
45160  * @constructor
45161  * Create a new Panel, that can contain a layout.Border.
45162  * 
45163  * 
45164  * @param {String/Object} config A string to set only the title or a config object
45165  */
45166 Roo.bootstrap.panel.Nest = function(config)
45167 {
45168     // construct with only one argument..
45169     /* FIXME - implement nicer consturctors
45170     if (layout.layout) {
45171         config = layout;
45172         layout = config.layout;
45173         delete config.layout;
45174     }
45175     if (layout.xtype && !layout.getEl) {
45176         // then layout needs constructing..
45177         layout = Roo.factory(layout, Roo);
45178     }
45179     */
45180     
45181     config.el =  config.layout.getEl();
45182     
45183     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45184     
45185     config.layout.monitorWindowResize = false; // turn off autosizing
45186     this.layout = config.layout;
45187     this.layout.getEl().addClass("roo-layout-nested-layout");
45188     this.layout.parent = this;
45189     
45190     
45191     
45192     
45193 };
45194
45195 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45196     /**
45197     * @cfg {Roo.BorderLayout} layout The layout for this panel
45198     */
45199     layout : false,
45200
45201     setSize : function(width, height){
45202         if(!this.ignoreResize(width, height)){
45203             var size = this.adjustForComponents(width, height);
45204             var el = this.layout.getEl();
45205             if (size.height < 1) {
45206                 el.setWidth(size.width);   
45207             } else {
45208                 el.setSize(size.width, size.height);
45209             }
45210             var touch = el.dom.offsetWidth;
45211             this.layout.layout();
45212             // ie requires a double layout on the first pass
45213             if(Roo.isIE && !this.initialized){
45214                 this.initialized = true;
45215                 this.layout.layout();
45216             }
45217         }
45218     },
45219     
45220     // activate all subpanels if not currently active..
45221     
45222     setActiveState : function(active){
45223         this.active = active;
45224         this.setActiveClass(active);
45225         
45226         if(!active){
45227             this.fireEvent("deactivate", this);
45228             return;
45229         }
45230         
45231         this.fireEvent("activate", this);
45232         // not sure if this should happen before or after..
45233         if (!this.layout) {
45234             return; // should not happen..
45235         }
45236         var reg = false;
45237         for (var r in this.layout.regions) {
45238             reg = this.layout.getRegion(r);
45239             if (reg.getActivePanel()) {
45240                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45241                 reg.setActivePanel(reg.getActivePanel());
45242                 continue;
45243             }
45244             if (!reg.panels.length) {
45245                 continue;
45246             }
45247             reg.showPanel(reg.getPanel(0));
45248         }
45249         
45250         
45251         
45252         
45253     },
45254     
45255     /**
45256      * Returns the nested BorderLayout for this panel
45257      * @return {Roo.BorderLayout} 
45258      */
45259     getLayout : function(){
45260         return this.layout;
45261     },
45262     
45263      /**
45264      * Adds a xtype elements to the layout of the nested panel
45265      * <pre><code>
45266
45267 panel.addxtype({
45268        xtype : 'ContentPanel',
45269        region: 'west',
45270        items: [ .... ]
45271    }
45272 );
45273
45274 panel.addxtype({
45275         xtype : 'NestedLayoutPanel',
45276         region: 'west',
45277         layout: {
45278            center: { },
45279            west: { }   
45280         },
45281         items : [ ... list of content panels or nested layout panels.. ]
45282    }
45283 );
45284 </code></pre>
45285      * @param {Object} cfg Xtype definition of item to add.
45286      */
45287     addxtype : function(cfg) {
45288         return this.layout.addxtype(cfg);
45289     
45290     }
45291 });/*
45292  * Based on:
45293  * Ext JS Library 1.1.1
45294  * Copyright(c) 2006-2007, Ext JS, LLC.
45295  *
45296  * Originally Released Under LGPL - original licence link has changed is not relivant.
45297  *
45298  * Fork - LGPL
45299  * <script type="text/javascript">
45300  */
45301 /**
45302  * @class Roo.TabPanel
45303  * @extends Roo.util.Observable
45304  * A lightweight tab container.
45305  * <br><br>
45306  * Usage:
45307  * <pre><code>
45308 // basic tabs 1, built from existing content
45309 var tabs = new Roo.TabPanel("tabs1");
45310 tabs.addTab("script", "View Script");
45311 tabs.addTab("markup", "View Markup");
45312 tabs.activate("script");
45313
45314 // more advanced tabs, built from javascript
45315 var jtabs = new Roo.TabPanel("jtabs");
45316 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45317
45318 // set up the UpdateManager
45319 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45320 var updater = tab2.getUpdateManager();
45321 updater.setDefaultUrl("ajax1.htm");
45322 tab2.on('activate', updater.refresh, updater, true);
45323
45324 // Use setUrl for Ajax loading
45325 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45326 tab3.setUrl("ajax2.htm", null, true);
45327
45328 // Disabled tab
45329 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45330 tab4.disable();
45331
45332 jtabs.activate("jtabs-1");
45333  * </code></pre>
45334  * @constructor
45335  * Create a new TabPanel.
45336  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45337  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45338  */
45339 Roo.bootstrap.panel.Tabs = function(config){
45340     /**
45341     * The container element for this TabPanel.
45342     * @type Roo.Element
45343     */
45344     this.el = Roo.get(config.el);
45345     delete config.el;
45346     if(config){
45347         if(typeof config == "boolean"){
45348             this.tabPosition = config ? "bottom" : "top";
45349         }else{
45350             Roo.apply(this, config);
45351         }
45352     }
45353     
45354     if(this.tabPosition == "bottom"){
45355         // if tabs are at the bottom = create the body first.
45356         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45357         this.el.addClass("roo-tabs-bottom");
45358     }
45359     // next create the tabs holders
45360     
45361     if (this.tabPosition == "west"){
45362         
45363         var reg = this.region; // fake it..
45364         while (reg) {
45365             if (!reg.mgr.parent) {
45366                 break;
45367             }
45368             reg = reg.mgr.parent.region;
45369         }
45370         Roo.log("got nest?");
45371         Roo.log(reg);
45372         if (reg.mgr.getRegion('west')) {
45373             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45374             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45375             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45376             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45377             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45378         
45379             
45380         }
45381         
45382         
45383     } else {
45384      
45385         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45386         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45387         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45388         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45389     }
45390     
45391     
45392     if(Roo.isIE){
45393         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45394     }
45395     
45396     // finally - if tabs are at the top, then create the body last..
45397     if(this.tabPosition != "bottom"){
45398         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45399          * @type Roo.Element
45400          */
45401         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45402         this.el.addClass("roo-tabs-top");
45403     }
45404     this.items = [];
45405
45406     this.bodyEl.setStyle("position", "relative");
45407
45408     this.active = null;
45409     this.activateDelegate = this.activate.createDelegate(this);
45410
45411     this.addEvents({
45412         /**
45413          * @event tabchange
45414          * Fires when the active tab changes
45415          * @param {Roo.TabPanel} this
45416          * @param {Roo.TabPanelItem} activePanel The new active tab
45417          */
45418         "tabchange": true,
45419         /**
45420          * @event beforetabchange
45421          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45422          * @param {Roo.TabPanel} this
45423          * @param {Object} e Set cancel to true on this object to cancel the tab change
45424          * @param {Roo.TabPanelItem} tab The tab being changed to
45425          */
45426         "beforetabchange" : true
45427     });
45428
45429     Roo.EventManager.onWindowResize(this.onResize, this);
45430     this.cpad = this.el.getPadding("lr");
45431     this.hiddenCount = 0;
45432
45433
45434     // toolbar on the tabbar support...
45435     if (this.toolbar) {
45436         alert("no toolbar support yet");
45437         this.toolbar  = false;
45438         /*
45439         var tcfg = this.toolbar;
45440         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
45441         this.toolbar = new Roo.Toolbar(tcfg);
45442         if (Roo.isSafari) {
45443             var tbl = tcfg.container.child('table', true);
45444             tbl.setAttribute('width', '100%');
45445         }
45446         */
45447         
45448     }
45449    
45450
45451
45452     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45453 };
45454
45455 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45456     /*
45457      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45458      */
45459     tabPosition : "top",
45460     /*
45461      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45462      */
45463     currentTabWidth : 0,
45464     /*
45465      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45466      */
45467     minTabWidth : 40,
45468     /*
45469      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45470      */
45471     maxTabWidth : 250,
45472     /*
45473      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45474      */
45475     preferredTabWidth : 175,
45476     /*
45477      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45478      */
45479     resizeTabs : false,
45480     /*
45481      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45482      */
45483     monitorResize : true,
45484     /*
45485      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
45486      */
45487     toolbar : false,  // set by caller..
45488     
45489     region : false, /// set by caller
45490     
45491     disableTooltips : true, // not used yet...
45492
45493     /**
45494      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45495      * @param {String} id The id of the div to use <b>or create</b>
45496      * @param {String} text The text for the tab
45497      * @param {String} content (optional) Content to put in the TabPanelItem body
45498      * @param {Boolean} closable (optional) True to create a close icon on the tab
45499      * @return {Roo.TabPanelItem} The created TabPanelItem
45500      */
45501     addTab : function(id, text, content, closable, tpl)
45502     {
45503         var item = new Roo.bootstrap.panel.TabItem({
45504             panel: this,
45505             id : id,
45506             text : text,
45507             closable : closable,
45508             tpl : tpl
45509         });
45510         this.addTabItem(item);
45511         if(content){
45512             item.setContent(content);
45513         }
45514         return item;
45515     },
45516
45517     /**
45518      * Returns the {@link Roo.TabPanelItem} with the specified id/index
45519      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45520      * @return {Roo.TabPanelItem}
45521      */
45522     getTab : function(id){
45523         return this.items[id];
45524     },
45525
45526     /**
45527      * Hides the {@link Roo.TabPanelItem} with the specified id/index
45528      * @param {String/Number} id The id or index of the TabPanelItem to hide.
45529      */
45530     hideTab : function(id){
45531         var t = this.items[id];
45532         if(!t.isHidden()){
45533            t.setHidden(true);
45534            this.hiddenCount++;
45535            this.autoSizeTabs();
45536         }
45537     },
45538
45539     /**
45540      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
45541      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
45542      */
45543     unhideTab : function(id){
45544         var t = this.items[id];
45545         if(t.isHidden()){
45546            t.setHidden(false);
45547            this.hiddenCount--;
45548            this.autoSizeTabs();
45549         }
45550     },
45551
45552     /**
45553      * Adds an existing {@link Roo.TabPanelItem}.
45554      * @param {Roo.TabPanelItem} item The TabPanelItem to add
45555      */
45556     addTabItem : function(item)
45557     {
45558         this.items[item.id] = item;
45559         this.items.push(item);
45560         this.autoSizeTabs();
45561       //  if(this.resizeTabs){
45562     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
45563   //         this.autoSizeTabs();
45564 //        }else{
45565 //            item.autoSize();
45566        // }
45567     },
45568
45569     /**
45570      * Removes a {@link Roo.TabPanelItem}.
45571      * @param {String/Number} id The id or index of the TabPanelItem to remove.
45572      */
45573     removeTab : function(id){
45574         var items = this.items;
45575         var tab = items[id];
45576         if(!tab) { return; }
45577         var index = items.indexOf(tab);
45578         if(this.active == tab && items.length > 1){
45579             var newTab = this.getNextAvailable(index);
45580             if(newTab) {
45581                 newTab.activate();
45582             }
45583         }
45584         this.stripEl.dom.removeChild(tab.pnode.dom);
45585         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
45586             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
45587         }
45588         items.splice(index, 1);
45589         delete this.items[tab.id];
45590         tab.fireEvent("close", tab);
45591         tab.purgeListeners();
45592         this.autoSizeTabs();
45593     },
45594
45595     getNextAvailable : function(start){
45596         var items = this.items;
45597         var index = start;
45598         // look for a next tab that will slide over to
45599         // replace the one being removed
45600         while(index < items.length){
45601             var item = items[++index];
45602             if(item && !item.isHidden()){
45603                 return item;
45604             }
45605         }
45606         // if one isn't found select the previous tab (on the left)
45607         index = start;
45608         while(index >= 0){
45609             var item = items[--index];
45610             if(item && !item.isHidden()){
45611                 return item;
45612             }
45613         }
45614         return null;
45615     },
45616
45617     /**
45618      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
45619      * @param {String/Number} id The id or index of the TabPanelItem to disable.
45620      */
45621     disableTab : function(id){
45622         var tab = this.items[id];
45623         if(tab && this.active != tab){
45624             tab.disable();
45625         }
45626     },
45627
45628     /**
45629      * Enables a {@link Roo.TabPanelItem} that is disabled.
45630      * @param {String/Number} id The id or index of the TabPanelItem to enable.
45631      */
45632     enableTab : function(id){
45633         var tab = this.items[id];
45634         tab.enable();
45635     },
45636
45637     /**
45638      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
45639      * @param {String/Number} id The id or index of the TabPanelItem to activate.
45640      * @return {Roo.TabPanelItem} The TabPanelItem.
45641      */
45642     activate : function(id)
45643     {
45644         //Roo.log('activite:'  + id);
45645         
45646         var tab = this.items[id];
45647         if(!tab){
45648             return null;
45649         }
45650         if(tab == this.active || tab.disabled){
45651             return tab;
45652         }
45653         var e = {};
45654         this.fireEvent("beforetabchange", this, e, tab);
45655         if(e.cancel !== true && !tab.disabled){
45656             if(this.active){
45657                 this.active.hide();
45658             }
45659             this.active = this.items[id];
45660             this.active.show();
45661             this.fireEvent("tabchange", this, this.active);
45662         }
45663         return tab;
45664     },
45665
45666     /**
45667      * Gets the active {@link Roo.TabPanelItem}.
45668      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
45669      */
45670     getActiveTab : function(){
45671         return this.active;
45672     },
45673
45674     /**
45675      * Updates the tab body element to fit the height of the container element
45676      * for overflow scrolling
45677      * @param {Number} targetHeight (optional) Override the starting height from the elements height
45678      */
45679     syncHeight : function(targetHeight){
45680         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45681         var bm = this.bodyEl.getMargins();
45682         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
45683         this.bodyEl.setHeight(newHeight);
45684         return newHeight;
45685     },
45686
45687     onResize : function(){
45688         if(this.monitorResize){
45689             this.autoSizeTabs();
45690         }
45691     },
45692
45693     /**
45694      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
45695      */
45696     beginUpdate : function(){
45697         this.updating = true;
45698     },
45699
45700     /**
45701      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
45702      */
45703     endUpdate : function(){
45704         this.updating = false;
45705         this.autoSizeTabs();
45706     },
45707
45708     /**
45709      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
45710      */
45711     autoSizeTabs : function()
45712     {
45713         var count = this.items.length;
45714         var vcount = count - this.hiddenCount;
45715         
45716         if (vcount < 2) {
45717             this.stripEl.hide();
45718         } else {
45719             this.stripEl.show();
45720         }
45721         
45722         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
45723             return;
45724         }
45725         
45726         
45727         var w = Math.max(this.el.getWidth() - this.cpad, 10);
45728         var availWidth = Math.floor(w / vcount);
45729         var b = this.stripBody;
45730         if(b.getWidth() > w){
45731             var tabs = this.items;
45732             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
45733             if(availWidth < this.minTabWidth){
45734                 /*if(!this.sleft){    // incomplete scrolling code
45735                     this.createScrollButtons();
45736                 }
45737                 this.showScroll();
45738                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
45739             }
45740         }else{
45741             if(this.currentTabWidth < this.preferredTabWidth){
45742                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
45743             }
45744         }
45745     },
45746
45747     /**
45748      * Returns the number of tabs in this TabPanel.
45749      * @return {Number}
45750      */
45751      getCount : function(){
45752          return this.items.length;
45753      },
45754
45755     /**
45756      * Resizes all the tabs to the passed width
45757      * @param {Number} The new width
45758      */
45759     setTabWidth : function(width){
45760         this.currentTabWidth = width;
45761         for(var i = 0, len = this.items.length; i < len; i++) {
45762                 if(!this.items[i].isHidden()) {
45763                 this.items[i].setWidth(width);
45764             }
45765         }
45766     },
45767
45768     /**
45769      * Destroys this TabPanel
45770      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
45771      */
45772     destroy : function(removeEl){
45773         Roo.EventManager.removeResizeListener(this.onResize, this);
45774         for(var i = 0, len = this.items.length; i < len; i++){
45775             this.items[i].purgeListeners();
45776         }
45777         if(removeEl === true){
45778             this.el.update("");
45779             this.el.remove();
45780         }
45781     },
45782     
45783     createStrip : function(container)
45784     {
45785         var strip = document.createElement("nav");
45786         strip.className = Roo.bootstrap.version == 4 ?
45787             "navbar-light bg-light" : 
45788             "navbar navbar-default"; //"x-tabs-wrap";
45789         container.appendChild(strip);
45790         return strip;
45791     },
45792     
45793     createStripList : function(strip)
45794     {
45795         // div wrapper for retard IE
45796         // returns the "tr" element.
45797         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
45798         //'<div class="x-tabs-strip-wrap">'+
45799           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
45800           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
45801         return strip.firstChild; //.firstChild.firstChild.firstChild;
45802     },
45803     createBody : function(container)
45804     {
45805         var body = document.createElement("div");
45806         Roo.id(body, "tab-body");
45807         //Roo.fly(body).addClass("x-tabs-body");
45808         Roo.fly(body).addClass("tab-content");
45809         container.appendChild(body);
45810         return body;
45811     },
45812     createItemBody :function(bodyEl, id){
45813         var body = Roo.getDom(id);
45814         if(!body){
45815             body = document.createElement("div");
45816             body.id = id;
45817         }
45818         //Roo.fly(body).addClass("x-tabs-item-body");
45819         Roo.fly(body).addClass("tab-pane");
45820          bodyEl.insertBefore(body, bodyEl.firstChild);
45821         return body;
45822     },
45823     /** @private */
45824     createStripElements :  function(stripEl, text, closable, tpl)
45825     {
45826         var td = document.createElement("li"); // was td..
45827         td.className = 'nav-item';
45828         
45829         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
45830         
45831         
45832         stripEl.appendChild(td);
45833         /*if(closable){
45834             td.className = "x-tabs-closable";
45835             if(!this.closeTpl){
45836                 this.closeTpl = new Roo.Template(
45837                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45838                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
45839                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
45840                 );
45841             }
45842             var el = this.closeTpl.overwrite(td, {"text": text});
45843             var close = el.getElementsByTagName("div")[0];
45844             var inner = el.getElementsByTagName("em")[0];
45845             return {"el": el, "close": close, "inner": inner};
45846         } else {
45847         */
45848         // not sure what this is..
45849 //            if(!this.tabTpl){
45850                 //this.tabTpl = new Roo.Template(
45851                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45852                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
45853                 //);
45854 //                this.tabTpl = new Roo.Template(
45855 //                   '<a href="#">' +
45856 //                   '<span unselectable="on"' +
45857 //                            (this.disableTooltips ? '' : ' title="{text}"') +
45858 //                            ' >{text}</span></a>'
45859 //                );
45860 //                
45861 //            }
45862
45863
45864             var template = tpl || this.tabTpl || false;
45865             
45866             if(!template){
45867                 template =  new Roo.Template(
45868                         Roo.bootstrap.version == 4 ? 
45869                             (
45870                                 '<a class="nav-link" href="#" unselectable="on"' +
45871                                      (this.disableTooltips ? '' : ' title="{text}"') +
45872                                      ' >{text}</a>'
45873                             ) : (
45874                                 '<a class="nav-link" href="#">' +
45875                                 '<span unselectable="on"' +
45876                                          (this.disableTooltips ? '' : ' title="{text}"') +
45877                                     ' >{text}</span></a>'
45878                             )
45879                 );
45880             }
45881             
45882             switch (typeof(template)) {
45883                 case 'object' :
45884                     break;
45885                 case 'string' :
45886                     template = new Roo.Template(template);
45887                     break;
45888                 default :
45889                     break;
45890             }
45891             
45892             var el = template.overwrite(td, {"text": text});
45893             
45894             var inner = el.getElementsByTagName("span")[0];
45895             
45896             return {"el": el, "inner": inner};
45897             
45898     }
45899         
45900     
45901 });
45902
45903 /**
45904  * @class Roo.TabPanelItem
45905  * @extends Roo.util.Observable
45906  * Represents an individual item (tab plus body) in a TabPanel.
45907  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
45908  * @param {String} id The id of this TabPanelItem
45909  * @param {String} text The text for the tab of this TabPanelItem
45910  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
45911  */
45912 Roo.bootstrap.panel.TabItem = function(config){
45913     /**
45914      * The {@link Roo.TabPanel} this TabPanelItem belongs to
45915      * @type Roo.TabPanel
45916      */
45917     this.tabPanel = config.panel;
45918     /**
45919      * The id for this TabPanelItem
45920      * @type String
45921      */
45922     this.id = config.id;
45923     /** @private */
45924     this.disabled = false;
45925     /** @private */
45926     this.text = config.text;
45927     /** @private */
45928     this.loaded = false;
45929     this.closable = config.closable;
45930
45931     /**
45932      * The body element for this TabPanelItem.
45933      * @type Roo.Element
45934      */
45935     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
45936     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
45937     this.bodyEl.setStyle("display", "block");
45938     this.bodyEl.setStyle("zoom", "1");
45939     //this.hideAction();
45940
45941     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
45942     /** @private */
45943     this.el = Roo.get(els.el);
45944     this.inner = Roo.get(els.inner, true);
45945      this.textEl = Roo.bootstrap.version == 4 ?
45946         this.el : Roo.get(this.el.dom.firstChild, true);
45947
45948     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
45949     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
45950
45951     
45952 //    this.el.on("mousedown", this.onTabMouseDown, this);
45953     this.el.on("click", this.onTabClick, this);
45954     /** @private */
45955     if(config.closable){
45956         var c = Roo.get(els.close, true);
45957         c.dom.title = this.closeText;
45958         c.addClassOnOver("close-over");
45959         c.on("click", this.closeClick, this);
45960      }
45961
45962     this.addEvents({
45963          /**
45964          * @event activate
45965          * Fires when this tab becomes the active tab.
45966          * @param {Roo.TabPanel} tabPanel The parent TabPanel
45967          * @param {Roo.TabPanelItem} this
45968          */
45969         "activate": true,
45970         /**
45971          * @event beforeclose
45972          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
45973          * @param {Roo.TabPanelItem} this
45974          * @param {Object} e Set cancel to true on this object to cancel the close.
45975          */
45976         "beforeclose": true,
45977         /**
45978          * @event close
45979          * Fires when this tab is closed.
45980          * @param {Roo.TabPanelItem} this
45981          */
45982          "close": true,
45983         /**
45984          * @event deactivate
45985          * Fires when this tab is no longer the active tab.
45986          * @param {Roo.TabPanel} tabPanel The parent TabPanel
45987          * @param {Roo.TabPanelItem} this
45988          */
45989          "deactivate" : true
45990     });
45991     this.hidden = false;
45992
45993     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
45994 };
45995
45996 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
45997            {
45998     purgeListeners : function(){
45999        Roo.util.Observable.prototype.purgeListeners.call(this);
46000        this.el.removeAllListeners();
46001     },
46002     /**
46003      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46004      */
46005     show : function(){
46006         this.status_node.addClass("active");
46007         this.showAction();
46008         if(Roo.isOpera){
46009             this.tabPanel.stripWrap.repaint();
46010         }
46011         this.fireEvent("activate", this.tabPanel, this);
46012     },
46013
46014     /**
46015      * Returns true if this tab is the active tab.
46016      * @return {Boolean}
46017      */
46018     isActive : function(){
46019         return this.tabPanel.getActiveTab() == this;
46020     },
46021
46022     /**
46023      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46024      */
46025     hide : function(){
46026         this.status_node.removeClass("active");
46027         this.hideAction();
46028         this.fireEvent("deactivate", this.tabPanel, this);
46029     },
46030
46031     hideAction : function(){
46032         this.bodyEl.hide();
46033         this.bodyEl.setStyle("position", "absolute");
46034         this.bodyEl.setLeft("-20000px");
46035         this.bodyEl.setTop("-20000px");
46036     },
46037
46038     showAction : function(){
46039         this.bodyEl.setStyle("position", "relative");
46040         this.bodyEl.setTop("");
46041         this.bodyEl.setLeft("");
46042         this.bodyEl.show();
46043     },
46044
46045     /**
46046      * Set the tooltip for the tab.
46047      * @param {String} tooltip The tab's tooltip
46048      */
46049     setTooltip : function(text){
46050         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46051             this.textEl.dom.qtip = text;
46052             this.textEl.dom.removeAttribute('title');
46053         }else{
46054             this.textEl.dom.title = text;
46055         }
46056     },
46057
46058     onTabClick : function(e){
46059         e.preventDefault();
46060         this.tabPanel.activate(this.id);
46061     },
46062
46063     onTabMouseDown : function(e){
46064         e.preventDefault();
46065         this.tabPanel.activate(this.id);
46066     },
46067 /*
46068     getWidth : function(){
46069         return this.inner.getWidth();
46070     },
46071
46072     setWidth : function(width){
46073         var iwidth = width - this.linode.getPadding("lr");
46074         this.inner.setWidth(iwidth);
46075         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46076         this.linode.setWidth(width);
46077     },
46078 */
46079     /**
46080      * Show or hide the tab
46081      * @param {Boolean} hidden True to hide or false to show.
46082      */
46083     setHidden : function(hidden){
46084         this.hidden = hidden;
46085         this.linode.setStyle("display", hidden ? "none" : "");
46086     },
46087
46088     /**
46089      * Returns true if this tab is "hidden"
46090      * @return {Boolean}
46091      */
46092     isHidden : function(){
46093         return this.hidden;
46094     },
46095
46096     /**
46097      * Returns the text for this tab
46098      * @return {String}
46099      */
46100     getText : function(){
46101         return this.text;
46102     },
46103     /*
46104     autoSize : function(){
46105         //this.el.beginMeasure();
46106         this.textEl.setWidth(1);
46107         /*
46108          *  #2804 [new] Tabs in Roojs
46109          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46110          */
46111         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46112         //this.el.endMeasure();
46113     //},
46114
46115     /**
46116      * Sets the text for the tab (Note: this also sets the tooltip text)
46117      * @param {String} text The tab's text and tooltip
46118      */
46119     setText : function(text){
46120         this.text = text;
46121         this.textEl.update(text);
46122         this.setTooltip(text);
46123         //if(!this.tabPanel.resizeTabs){
46124         //    this.autoSize();
46125         //}
46126     },
46127     /**
46128      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46129      */
46130     activate : function(){
46131         this.tabPanel.activate(this.id);
46132     },
46133
46134     /**
46135      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46136      */
46137     disable : function(){
46138         if(this.tabPanel.active != this){
46139             this.disabled = true;
46140             this.status_node.addClass("disabled");
46141         }
46142     },
46143
46144     /**
46145      * Enables this TabPanelItem if it was previously disabled.
46146      */
46147     enable : function(){
46148         this.disabled = false;
46149         this.status_node.removeClass("disabled");
46150     },
46151
46152     /**
46153      * Sets the content for this TabPanelItem.
46154      * @param {String} content The content
46155      * @param {Boolean} loadScripts true to look for and load scripts
46156      */
46157     setContent : function(content, loadScripts){
46158         this.bodyEl.update(content, loadScripts);
46159     },
46160
46161     /**
46162      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46163      * @return {Roo.UpdateManager} The UpdateManager
46164      */
46165     getUpdateManager : function(){
46166         return this.bodyEl.getUpdateManager();
46167     },
46168
46169     /**
46170      * Set a URL to be used to load the content for this TabPanelItem.
46171      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46172      * @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)
46173      * @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)
46174      * @return {Roo.UpdateManager} The UpdateManager
46175      */
46176     setUrl : function(url, params, loadOnce){
46177         if(this.refreshDelegate){
46178             this.un('activate', this.refreshDelegate);
46179         }
46180         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46181         this.on("activate", this.refreshDelegate);
46182         return this.bodyEl.getUpdateManager();
46183     },
46184
46185     /** @private */
46186     _handleRefresh : function(url, params, loadOnce){
46187         if(!loadOnce || !this.loaded){
46188             var updater = this.bodyEl.getUpdateManager();
46189             updater.update(url, params, this._setLoaded.createDelegate(this));
46190         }
46191     },
46192
46193     /**
46194      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46195      *   Will fail silently if the setUrl method has not been called.
46196      *   This does not activate the panel, just updates its content.
46197      */
46198     refresh : function(){
46199         if(this.refreshDelegate){
46200            this.loaded = false;
46201            this.refreshDelegate();
46202         }
46203     },
46204
46205     /** @private */
46206     _setLoaded : function(){
46207         this.loaded = true;
46208     },
46209
46210     /** @private */
46211     closeClick : function(e){
46212         var o = {};
46213         e.stopEvent();
46214         this.fireEvent("beforeclose", this, o);
46215         if(o.cancel !== true){
46216             this.tabPanel.removeTab(this.id);
46217         }
46218     },
46219     /**
46220      * The text displayed in the tooltip for the close icon.
46221      * @type String
46222      */
46223     closeText : "Close this tab"
46224 });
46225 /**
46226 *    This script refer to:
46227 *    Title: International Telephone Input
46228 *    Author: Jack O'Connor
46229 *    Code version:  v12.1.12
46230 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46231 **/
46232
46233 Roo.bootstrap.form.PhoneInputData = function() {
46234     var d = [
46235       [
46236         "Afghanistan (‫افغانستان‬‎)",
46237         "af",
46238         "93"
46239       ],
46240       [
46241         "Albania (Shqipëri)",
46242         "al",
46243         "355"
46244       ],
46245       [
46246         "Algeria (‫الجزائر‬‎)",
46247         "dz",
46248         "213"
46249       ],
46250       [
46251         "American Samoa",
46252         "as",
46253         "1684"
46254       ],
46255       [
46256         "Andorra",
46257         "ad",
46258         "376"
46259       ],
46260       [
46261         "Angola",
46262         "ao",
46263         "244"
46264       ],
46265       [
46266         "Anguilla",
46267         "ai",
46268         "1264"
46269       ],
46270       [
46271         "Antigua and Barbuda",
46272         "ag",
46273         "1268"
46274       ],
46275       [
46276         "Argentina",
46277         "ar",
46278         "54"
46279       ],
46280       [
46281         "Armenia (Հայաստան)",
46282         "am",
46283         "374"
46284       ],
46285       [
46286         "Aruba",
46287         "aw",
46288         "297"
46289       ],
46290       [
46291         "Australia",
46292         "au",
46293         "61",
46294         0
46295       ],
46296       [
46297         "Austria (Österreich)",
46298         "at",
46299         "43"
46300       ],
46301       [
46302         "Azerbaijan (Azərbaycan)",
46303         "az",
46304         "994"
46305       ],
46306       [
46307         "Bahamas",
46308         "bs",
46309         "1242"
46310       ],
46311       [
46312         "Bahrain (‫البحرين‬‎)",
46313         "bh",
46314         "973"
46315       ],
46316       [
46317         "Bangladesh (বাংলাদেশ)",
46318         "bd",
46319         "880"
46320       ],
46321       [
46322         "Barbados",
46323         "bb",
46324         "1246"
46325       ],
46326       [
46327         "Belarus (Беларусь)",
46328         "by",
46329         "375"
46330       ],
46331       [
46332         "Belgium (België)",
46333         "be",
46334         "32"
46335       ],
46336       [
46337         "Belize",
46338         "bz",
46339         "501"
46340       ],
46341       [
46342         "Benin (Bénin)",
46343         "bj",
46344         "229"
46345       ],
46346       [
46347         "Bermuda",
46348         "bm",
46349         "1441"
46350       ],
46351       [
46352         "Bhutan (འབྲུག)",
46353         "bt",
46354         "975"
46355       ],
46356       [
46357         "Bolivia",
46358         "bo",
46359         "591"
46360       ],
46361       [
46362         "Bosnia and Herzegovina (Босна и Херцеговина)",
46363         "ba",
46364         "387"
46365       ],
46366       [
46367         "Botswana",
46368         "bw",
46369         "267"
46370       ],
46371       [
46372         "Brazil (Brasil)",
46373         "br",
46374         "55"
46375       ],
46376       [
46377         "British Indian Ocean Territory",
46378         "io",
46379         "246"
46380       ],
46381       [
46382         "British Virgin Islands",
46383         "vg",
46384         "1284"
46385       ],
46386       [
46387         "Brunei",
46388         "bn",
46389         "673"
46390       ],
46391       [
46392         "Bulgaria (България)",
46393         "bg",
46394         "359"
46395       ],
46396       [
46397         "Burkina Faso",
46398         "bf",
46399         "226"
46400       ],
46401       [
46402         "Burundi (Uburundi)",
46403         "bi",
46404         "257"
46405       ],
46406       [
46407         "Cambodia (កម្ពុជា)",
46408         "kh",
46409         "855"
46410       ],
46411       [
46412         "Cameroon (Cameroun)",
46413         "cm",
46414         "237"
46415       ],
46416       [
46417         "Canada",
46418         "ca",
46419         "1",
46420         1,
46421         ["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"]
46422       ],
46423       [
46424         "Cape Verde (Kabu Verdi)",
46425         "cv",
46426         "238"
46427       ],
46428       [
46429         "Caribbean Netherlands",
46430         "bq",
46431         "599",
46432         1
46433       ],
46434       [
46435         "Cayman Islands",
46436         "ky",
46437         "1345"
46438       ],
46439       [
46440         "Central African Republic (République centrafricaine)",
46441         "cf",
46442         "236"
46443       ],
46444       [
46445         "Chad (Tchad)",
46446         "td",
46447         "235"
46448       ],
46449       [
46450         "Chile",
46451         "cl",
46452         "56"
46453       ],
46454       [
46455         "China (中国)",
46456         "cn",
46457         "86"
46458       ],
46459       [
46460         "Christmas Island",
46461         "cx",
46462         "61",
46463         2
46464       ],
46465       [
46466         "Cocos (Keeling) Islands",
46467         "cc",
46468         "61",
46469         1
46470       ],
46471       [
46472         "Colombia",
46473         "co",
46474         "57"
46475       ],
46476       [
46477         "Comoros (‫جزر القمر‬‎)",
46478         "km",
46479         "269"
46480       ],
46481       [
46482         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46483         "cd",
46484         "243"
46485       ],
46486       [
46487         "Congo (Republic) (Congo-Brazzaville)",
46488         "cg",
46489         "242"
46490       ],
46491       [
46492         "Cook Islands",
46493         "ck",
46494         "682"
46495       ],
46496       [
46497         "Costa Rica",
46498         "cr",
46499         "506"
46500       ],
46501       [
46502         "Côte d’Ivoire",
46503         "ci",
46504         "225"
46505       ],
46506       [
46507         "Croatia (Hrvatska)",
46508         "hr",
46509         "385"
46510       ],
46511       [
46512         "Cuba",
46513         "cu",
46514         "53"
46515       ],
46516       [
46517         "Curaçao",
46518         "cw",
46519         "599",
46520         0
46521       ],
46522       [
46523         "Cyprus (Κύπρος)",
46524         "cy",
46525         "357"
46526       ],
46527       [
46528         "Czech Republic (Česká republika)",
46529         "cz",
46530         "420"
46531       ],
46532       [
46533         "Denmark (Danmark)",
46534         "dk",
46535         "45"
46536       ],
46537       [
46538         "Djibouti",
46539         "dj",
46540         "253"
46541       ],
46542       [
46543         "Dominica",
46544         "dm",
46545         "1767"
46546       ],
46547       [
46548         "Dominican Republic (República Dominicana)",
46549         "do",
46550         "1",
46551         2,
46552         ["809", "829", "849"]
46553       ],
46554       [
46555         "Ecuador",
46556         "ec",
46557         "593"
46558       ],
46559       [
46560         "Egypt (‫مصر‬‎)",
46561         "eg",
46562         "20"
46563       ],
46564       [
46565         "El Salvador",
46566         "sv",
46567         "503"
46568       ],
46569       [
46570         "Equatorial Guinea (Guinea Ecuatorial)",
46571         "gq",
46572         "240"
46573       ],
46574       [
46575         "Eritrea",
46576         "er",
46577         "291"
46578       ],
46579       [
46580         "Estonia (Eesti)",
46581         "ee",
46582         "372"
46583       ],
46584       [
46585         "Ethiopia",
46586         "et",
46587         "251"
46588       ],
46589       [
46590         "Falkland Islands (Islas Malvinas)",
46591         "fk",
46592         "500"
46593       ],
46594       [
46595         "Faroe Islands (Føroyar)",
46596         "fo",
46597         "298"
46598       ],
46599       [
46600         "Fiji",
46601         "fj",
46602         "679"
46603       ],
46604       [
46605         "Finland (Suomi)",
46606         "fi",
46607         "358",
46608         0
46609       ],
46610       [
46611         "France",
46612         "fr",
46613         "33"
46614       ],
46615       [
46616         "French Guiana (Guyane française)",
46617         "gf",
46618         "594"
46619       ],
46620       [
46621         "French Polynesia (Polynésie française)",
46622         "pf",
46623         "689"
46624       ],
46625       [
46626         "Gabon",
46627         "ga",
46628         "241"
46629       ],
46630       [
46631         "Gambia",
46632         "gm",
46633         "220"
46634       ],
46635       [
46636         "Georgia (საქართველო)",
46637         "ge",
46638         "995"
46639       ],
46640       [
46641         "Germany (Deutschland)",
46642         "de",
46643         "49"
46644       ],
46645       [
46646         "Ghana (Gaana)",
46647         "gh",
46648         "233"
46649       ],
46650       [
46651         "Gibraltar",
46652         "gi",
46653         "350"
46654       ],
46655       [
46656         "Greece (Ελλάδα)",
46657         "gr",
46658         "30"
46659       ],
46660       [
46661         "Greenland (Kalaallit Nunaat)",
46662         "gl",
46663         "299"
46664       ],
46665       [
46666         "Grenada",
46667         "gd",
46668         "1473"
46669       ],
46670       [
46671         "Guadeloupe",
46672         "gp",
46673         "590",
46674         0
46675       ],
46676       [
46677         "Guam",
46678         "gu",
46679         "1671"
46680       ],
46681       [
46682         "Guatemala",
46683         "gt",
46684         "502"
46685       ],
46686       [
46687         "Guernsey",
46688         "gg",
46689         "44",
46690         1
46691       ],
46692       [
46693         "Guinea (Guinée)",
46694         "gn",
46695         "224"
46696       ],
46697       [
46698         "Guinea-Bissau (Guiné Bissau)",
46699         "gw",
46700         "245"
46701       ],
46702       [
46703         "Guyana",
46704         "gy",
46705         "592"
46706       ],
46707       [
46708         "Haiti",
46709         "ht",
46710         "509"
46711       ],
46712       [
46713         "Honduras",
46714         "hn",
46715         "504"
46716       ],
46717       [
46718         "Hong Kong (香港)",
46719         "hk",
46720         "852"
46721       ],
46722       [
46723         "Hungary (Magyarország)",
46724         "hu",
46725         "36"
46726       ],
46727       [
46728         "Iceland (Ísland)",
46729         "is",
46730         "354"
46731       ],
46732       [
46733         "India (भारत)",
46734         "in",
46735         "91"
46736       ],
46737       [
46738         "Indonesia",
46739         "id",
46740         "62"
46741       ],
46742       [
46743         "Iran (‫ایران‬‎)",
46744         "ir",
46745         "98"
46746       ],
46747       [
46748         "Iraq (‫العراق‬‎)",
46749         "iq",
46750         "964"
46751       ],
46752       [
46753         "Ireland",
46754         "ie",
46755         "353"
46756       ],
46757       [
46758         "Isle of Man",
46759         "im",
46760         "44",
46761         2
46762       ],
46763       [
46764         "Israel (‫ישראל‬‎)",
46765         "il",
46766         "972"
46767       ],
46768       [
46769         "Italy (Italia)",
46770         "it",
46771         "39",
46772         0
46773       ],
46774       [
46775         "Jamaica",
46776         "jm",
46777         "1876"
46778       ],
46779       [
46780         "Japan (日本)",
46781         "jp",
46782         "81"
46783       ],
46784       [
46785         "Jersey",
46786         "je",
46787         "44",
46788         3
46789       ],
46790       [
46791         "Jordan (‫الأردن‬‎)",
46792         "jo",
46793         "962"
46794       ],
46795       [
46796         "Kazakhstan (Казахстан)",
46797         "kz",
46798         "7",
46799         1
46800       ],
46801       [
46802         "Kenya",
46803         "ke",
46804         "254"
46805       ],
46806       [
46807         "Kiribati",
46808         "ki",
46809         "686"
46810       ],
46811       [
46812         "Kosovo",
46813         "xk",
46814         "383"
46815       ],
46816       [
46817         "Kuwait (‫الكويت‬‎)",
46818         "kw",
46819         "965"
46820       ],
46821       [
46822         "Kyrgyzstan (Кыргызстан)",
46823         "kg",
46824         "996"
46825       ],
46826       [
46827         "Laos (ລາວ)",
46828         "la",
46829         "856"
46830       ],
46831       [
46832         "Latvia (Latvija)",
46833         "lv",
46834         "371"
46835       ],
46836       [
46837         "Lebanon (‫لبنان‬‎)",
46838         "lb",
46839         "961"
46840       ],
46841       [
46842         "Lesotho",
46843         "ls",
46844         "266"
46845       ],
46846       [
46847         "Liberia",
46848         "lr",
46849         "231"
46850       ],
46851       [
46852         "Libya (‫ليبيا‬‎)",
46853         "ly",
46854         "218"
46855       ],
46856       [
46857         "Liechtenstein",
46858         "li",
46859         "423"
46860       ],
46861       [
46862         "Lithuania (Lietuva)",
46863         "lt",
46864         "370"
46865       ],
46866       [
46867         "Luxembourg",
46868         "lu",
46869         "352"
46870       ],
46871       [
46872         "Macau (澳門)",
46873         "mo",
46874         "853"
46875       ],
46876       [
46877         "Macedonia (FYROM) (Македонија)",
46878         "mk",
46879         "389"
46880       ],
46881       [
46882         "Madagascar (Madagasikara)",
46883         "mg",
46884         "261"
46885       ],
46886       [
46887         "Malawi",
46888         "mw",
46889         "265"
46890       ],
46891       [
46892         "Malaysia",
46893         "my",
46894         "60"
46895       ],
46896       [
46897         "Maldives",
46898         "mv",
46899         "960"
46900       ],
46901       [
46902         "Mali",
46903         "ml",
46904         "223"
46905       ],
46906       [
46907         "Malta",
46908         "mt",
46909         "356"
46910       ],
46911       [
46912         "Marshall Islands",
46913         "mh",
46914         "692"
46915       ],
46916       [
46917         "Martinique",
46918         "mq",
46919         "596"
46920       ],
46921       [
46922         "Mauritania (‫موريتانيا‬‎)",
46923         "mr",
46924         "222"
46925       ],
46926       [
46927         "Mauritius (Moris)",
46928         "mu",
46929         "230"
46930       ],
46931       [
46932         "Mayotte",
46933         "yt",
46934         "262",
46935         1
46936       ],
46937       [
46938         "Mexico (México)",
46939         "mx",
46940         "52"
46941       ],
46942       [
46943         "Micronesia",
46944         "fm",
46945         "691"
46946       ],
46947       [
46948         "Moldova (Republica Moldova)",
46949         "md",
46950         "373"
46951       ],
46952       [
46953         "Monaco",
46954         "mc",
46955         "377"
46956       ],
46957       [
46958         "Mongolia (Монгол)",
46959         "mn",
46960         "976"
46961       ],
46962       [
46963         "Montenegro (Crna Gora)",
46964         "me",
46965         "382"
46966       ],
46967       [
46968         "Montserrat",
46969         "ms",
46970         "1664"
46971       ],
46972       [
46973         "Morocco (‫المغرب‬‎)",
46974         "ma",
46975         "212",
46976         0
46977       ],
46978       [
46979         "Mozambique (Moçambique)",
46980         "mz",
46981         "258"
46982       ],
46983       [
46984         "Myanmar (Burma) (မြန်မာ)",
46985         "mm",
46986         "95"
46987       ],
46988       [
46989         "Namibia (Namibië)",
46990         "na",
46991         "264"
46992       ],
46993       [
46994         "Nauru",
46995         "nr",
46996         "674"
46997       ],
46998       [
46999         "Nepal (नेपाल)",
47000         "np",
47001         "977"
47002       ],
47003       [
47004         "Netherlands (Nederland)",
47005         "nl",
47006         "31"
47007       ],
47008       [
47009         "New Caledonia (Nouvelle-Calédonie)",
47010         "nc",
47011         "687"
47012       ],
47013       [
47014         "New Zealand",
47015         "nz",
47016         "64"
47017       ],
47018       [
47019         "Nicaragua",
47020         "ni",
47021         "505"
47022       ],
47023       [
47024         "Niger (Nijar)",
47025         "ne",
47026         "227"
47027       ],
47028       [
47029         "Nigeria",
47030         "ng",
47031         "234"
47032       ],
47033       [
47034         "Niue",
47035         "nu",
47036         "683"
47037       ],
47038       [
47039         "Norfolk Island",
47040         "nf",
47041         "672"
47042       ],
47043       [
47044         "North Korea (조선 민주주의 인민 공화국)",
47045         "kp",
47046         "850"
47047       ],
47048       [
47049         "Northern Mariana Islands",
47050         "mp",
47051         "1670"
47052       ],
47053       [
47054         "Norway (Norge)",
47055         "no",
47056         "47",
47057         0
47058       ],
47059       [
47060         "Oman (‫عُمان‬‎)",
47061         "om",
47062         "968"
47063       ],
47064       [
47065         "Pakistan (‫پاکستان‬‎)",
47066         "pk",
47067         "92"
47068       ],
47069       [
47070         "Palau",
47071         "pw",
47072         "680"
47073       ],
47074       [
47075         "Palestine (‫فلسطين‬‎)",
47076         "ps",
47077         "970"
47078       ],
47079       [
47080         "Panama (Panamá)",
47081         "pa",
47082         "507"
47083       ],
47084       [
47085         "Papua New Guinea",
47086         "pg",
47087         "675"
47088       ],
47089       [
47090         "Paraguay",
47091         "py",
47092         "595"
47093       ],
47094       [
47095         "Peru (Perú)",
47096         "pe",
47097         "51"
47098       ],
47099       [
47100         "Philippines",
47101         "ph",
47102         "63"
47103       ],
47104       [
47105         "Poland (Polska)",
47106         "pl",
47107         "48"
47108       ],
47109       [
47110         "Portugal",
47111         "pt",
47112         "351"
47113       ],
47114       [
47115         "Puerto Rico",
47116         "pr",
47117         "1",
47118         3,
47119         ["787", "939"]
47120       ],
47121       [
47122         "Qatar (‫قطر‬‎)",
47123         "qa",
47124         "974"
47125       ],
47126       [
47127         "Réunion (La Réunion)",
47128         "re",
47129         "262",
47130         0
47131       ],
47132       [
47133         "Romania (România)",
47134         "ro",
47135         "40"
47136       ],
47137       [
47138         "Russia (Россия)",
47139         "ru",
47140         "7",
47141         0
47142       ],
47143       [
47144         "Rwanda",
47145         "rw",
47146         "250"
47147       ],
47148       [
47149         "Saint Barthélemy",
47150         "bl",
47151         "590",
47152         1
47153       ],
47154       [
47155         "Saint Helena",
47156         "sh",
47157         "290"
47158       ],
47159       [
47160         "Saint Kitts and Nevis",
47161         "kn",
47162         "1869"
47163       ],
47164       [
47165         "Saint Lucia",
47166         "lc",
47167         "1758"
47168       ],
47169       [
47170         "Saint Martin (Saint-Martin (partie française))",
47171         "mf",
47172         "590",
47173         2
47174       ],
47175       [
47176         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47177         "pm",
47178         "508"
47179       ],
47180       [
47181         "Saint Vincent and the Grenadines",
47182         "vc",
47183         "1784"
47184       ],
47185       [
47186         "Samoa",
47187         "ws",
47188         "685"
47189       ],
47190       [
47191         "San Marino",
47192         "sm",
47193         "378"
47194       ],
47195       [
47196         "São Tomé and Príncipe (São Tomé e Príncipe)",
47197         "st",
47198         "239"
47199       ],
47200       [
47201         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47202         "sa",
47203         "966"
47204       ],
47205       [
47206         "Senegal (Sénégal)",
47207         "sn",
47208         "221"
47209       ],
47210       [
47211         "Serbia (Србија)",
47212         "rs",
47213         "381"
47214       ],
47215       [
47216         "Seychelles",
47217         "sc",
47218         "248"
47219       ],
47220       [
47221         "Sierra Leone",
47222         "sl",
47223         "232"
47224       ],
47225       [
47226         "Singapore",
47227         "sg",
47228         "65"
47229       ],
47230       [
47231         "Sint Maarten",
47232         "sx",
47233         "1721"
47234       ],
47235       [
47236         "Slovakia (Slovensko)",
47237         "sk",
47238         "421"
47239       ],
47240       [
47241         "Slovenia (Slovenija)",
47242         "si",
47243         "386"
47244       ],
47245       [
47246         "Solomon Islands",
47247         "sb",
47248         "677"
47249       ],
47250       [
47251         "Somalia (Soomaaliya)",
47252         "so",
47253         "252"
47254       ],
47255       [
47256         "South Africa",
47257         "za",
47258         "27"
47259       ],
47260       [
47261         "South Korea (대한민국)",
47262         "kr",
47263         "82"
47264       ],
47265       [
47266         "South Sudan (‫جنوب السودان‬‎)",
47267         "ss",
47268         "211"
47269       ],
47270       [
47271         "Spain (España)",
47272         "es",
47273         "34"
47274       ],
47275       [
47276         "Sri Lanka (ශ්‍රී ලංකාව)",
47277         "lk",
47278         "94"
47279       ],
47280       [
47281         "Sudan (‫السودان‬‎)",
47282         "sd",
47283         "249"
47284       ],
47285       [
47286         "Suriname",
47287         "sr",
47288         "597"
47289       ],
47290       [
47291         "Svalbard and Jan Mayen",
47292         "sj",
47293         "47",
47294         1
47295       ],
47296       [
47297         "Swaziland",
47298         "sz",
47299         "268"
47300       ],
47301       [
47302         "Sweden (Sverige)",
47303         "se",
47304         "46"
47305       ],
47306       [
47307         "Switzerland (Schweiz)",
47308         "ch",
47309         "41"
47310       ],
47311       [
47312         "Syria (‫سوريا‬‎)",
47313         "sy",
47314         "963"
47315       ],
47316       [
47317         "Taiwan (台灣)",
47318         "tw",
47319         "886"
47320       ],
47321       [
47322         "Tajikistan",
47323         "tj",
47324         "992"
47325       ],
47326       [
47327         "Tanzania",
47328         "tz",
47329         "255"
47330       ],
47331       [
47332         "Thailand (ไทย)",
47333         "th",
47334         "66"
47335       ],
47336       [
47337         "Timor-Leste",
47338         "tl",
47339         "670"
47340       ],
47341       [
47342         "Togo",
47343         "tg",
47344         "228"
47345       ],
47346       [
47347         "Tokelau",
47348         "tk",
47349         "690"
47350       ],
47351       [
47352         "Tonga",
47353         "to",
47354         "676"
47355       ],
47356       [
47357         "Trinidad and Tobago",
47358         "tt",
47359         "1868"
47360       ],
47361       [
47362         "Tunisia (‫تونس‬‎)",
47363         "tn",
47364         "216"
47365       ],
47366       [
47367         "Turkey (Türkiye)",
47368         "tr",
47369         "90"
47370       ],
47371       [
47372         "Turkmenistan",
47373         "tm",
47374         "993"
47375       ],
47376       [
47377         "Turks and Caicos Islands",
47378         "tc",
47379         "1649"
47380       ],
47381       [
47382         "Tuvalu",
47383         "tv",
47384         "688"
47385       ],
47386       [
47387         "U.S. Virgin Islands",
47388         "vi",
47389         "1340"
47390       ],
47391       [
47392         "Uganda",
47393         "ug",
47394         "256"
47395       ],
47396       [
47397         "Ukraine (Україна)",
47398         "ua",
47399         "380"
47400       ],
47401       [
47402         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47403         "ae",
47404         "971"
47405       ],
47406       [
47407         "United Kingdom",
47408         "gb",
47409         "44",
47410         0
47411       ],
47412       [
47413         "United States",
47414         "us",
47415         "1",
47416         0
47417       ],
47418       [
47419         "Uruguay",
47420         "uy",
47421         "598"
47422       ],
47423       [
47424         "Uzbekistan (Oʻzbekiston)",
47425         "uz",
47426         "998"
47427       ],
47428       [
47429         "Vanuatu",
47430         "vu",
47431         "678"
47432       ],
47433       [
47434         "Vatican City (Città del Vaticano)",
47435         "va",
47436         "39",
47437         1
47438       ],
47439       [
47440         "Venezuela",
47441         "ve",
47442         "58"
47443       ],
47444       [
47445         "Vietnam (Việt Nam)",
47446         "vn",
47447         "84"
47448       ],
47449       [
47450         "Wallis and Futuna (Wallis-et-Futuna)",
47451         "wf",
47452         "681"
47453       ],
47454       [
47455         "Western Sahara (‫الصحراء الغربية‬‎)",
47456         "eh",
47457         "212",
47458         1
47459       ],
47460       [
47461         "Yemen (‫اليمن‬‎)",
47462         "ye",
47463         "967"
47464       ],
47465       [
47466         "Zambia",
47467         "zm",
47468         "260"
47469       ],
47470       [
47471         "Zimbabwe",
47472         "zw",
47473         "263"
47474       ],
47475       [
47476         "Åland Islands",
47477         "ax",
47478         "358",
47479         1
47480       ]
47481   ];
47482   
47483   return d;
47484 }/**
47485 *    This script refer to:
47486 *    Title: International Telephone Input
47487 *    Author: Jack O'Connor
47488 *    Code version:  v12.1.12
47489 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47490 **/
47491
47492 /**
47493  * @class Roo.bootstrap.form.PhoneInput
47494  * @extends Roo.bootstrap.form.TriggerField
47495  * An input with International dial-code selection
47496  
47497  * @cfg {String} defaultDialCode default '+852'
47498  * @cfg {Array} preferedCountries default []
47499   
47500  * @constructor
47501  * Create a new PhoneInput.
47502  * @param {Object} config Configuration options
47503  */
47504
47505 Roo.bootstrap.form.PhoneInput = function(config) {
47506     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47507 };
47508
47509 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47510         /**
47511         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47512         */
47513         listWidth: undefined,
47514         
47515         selectedClass: 'active',
47516         
47517         invalidClass : "has-warning",
47518         
47519         validClass: 'has-success',
47520         
47521         allowed: '0123456789',
47522         
47523         max_length: 15,
47524         
47525         /**
47526          * @cfg {String} defaultDialCode The default dial code when initializing the input
47527          */
47528         defaultDialCode: '+852',
47529         
47530         /**
47531          * @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
47532          */
47533         preferedCountries: false,
47534         
47535         getAutoCreate : function()
47536         {
47537             var data = Roo.bootstrap.form.PhoneInputData();
47538             var align = this.labelAlign || this.parentLabelAlign();
47539             var id = Roo.id();
47540             
47541             this.allCountries = [];
47542             this.dialCodeMapping = [];
47543             
47544             for (var i = 0; i < data.length; i++) {
47545               var c = data[i];
47546               this.allCountries[i] = {
47547                 name: c[0],
47548                 iso2: c[1],
47549                 dialCode: c[2],
47550                 priority: c[3] || 0,
47551                 areaCodes: c[4] || null
47552               };
47553               this.dialCodeMapping[c[2]] = {
47554                   name: c[0],
47555                   iso2: c[1],
47556                   priority: c[3] || 0,
47557                   areaCodes: c[4] || null
47558               };
47559             }
47560             
47561             var cfg = {
47562                 cls: 'form-group',
47563                 cn: []
47564             };
47565             
47566             var input =  {
47567                 tag: 'input',
47568                 id : id,
47569                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
47570                 maxlength: this.max_length,
47571                 cls : 'form-control tel-input',
47572                 autocomplete: 'new-password'
47573             };
47574             
47575             var hiddenInput = {
47576                 tag: 'input',
47577                 type: 'hidden',
47578                 cls: 'hidden-tel-input'
47579             };
47580             
47581             if (this.name) {
47582                 hiddenInput.name = this.name;
47583             }
47584             
47585             if (this.disabled) {
47586                 input.disabled = true;
47587             }
47588             
47589             var flag_container = {
47590                 tag: 'div',
47591                 cls: 'flag-box',
47592                 cn: [
47593                     {
47594                         tag: 'div',
47595                         cls: 'flag'
47596                     },
47597                     {
47598                         tag: 'div',
47599                         cls: 'caret'
47600                     }
47601                 ]
47602             };
47603             
47604             var box = {
47605                 tag: 'div',
47606                 cls: this.hasFeedback ? 'has-feedback' : '',
47607                 cn: [
47608                     hiddenInput,
47609                     input,
47610                     {
47611                         tag: 'input',
47612                         cls: 'dial-code-holder',
47613                         disabled: true
47614                     }
47615                 ]
47616             };
47617             
47618             var container = {
47619                 cls: 'roo-select2-container input-group',
47620                 cn: [
47621                     flag_container,
47622                     box
47623                 ]
47624             };
47625             
47626             if (this.fieldLabel.length) {
47627                 var indicator = {
47628                     tag: 'i',
47629                     tooltip: 'This field is required'
47630                 };
47631                 
47632                 var label = {
47633                     tag: 'label',
47634                     'for':  id,
47635                     cls: 'control-label',
47636                     cn: []
47637                 };
47638                 
47639                 var label_text = {
47640                     tag: 'span',
47641                     html: this.fieldLabel
47642                 };
47643                 
47644                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
47645                 label.cn = [
47646                     indicator,
47647                     label_text
47648                 ];
47649                 
47650                 if(this.indicatorpos == 'right') {
47651                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
47652                     label.cn = [
47653                         label_text,
47654                         indicator
47655                     ];
47656                 }
47657                 
47658                 if(align == 'left') {
47659                     container = {
47660                         tag: 'div',
47661                         cn: [
47662                             container
47663                         ]
47664                     };
47665                     
47666                     if(this.labelWidth > 12){
47667                         label.style = "width: " + this.labelWidth + 'px';
47668                     }
47669                     if(this.labelWidth < 13 && this.labelmd == 0){
47670                         this.labelmd = this.labelWidth;
47671                     }
47672                     if(this.labellg > 0){
47673                         label.cls += ' col-lg-' + this.labellg;
47674                         input.cls += ' col-lg-' + (12 - this.labellg);
47675                     }
47676                     if(this.labelmd > 0){
47677                         label.cls += ' col-md-' + this.labelmd;
47678                         container.cls += ' col-md-' + (12 - this.labelmd);
47679                     }
47680                     if(this.labelsm > 0){
47681                         label.cls += ' col-sm-' + this.labelsm;
47682                         container.cls += ' col-sm-' + (12 - this.labelsm);
47683                     }
47684                     if(this.labelxs > 0){
47685                         label.cls += ' col-xs-' + this.labelxs;
47686                         container.cls += ' col-xs-' + (12 - this.labelxs);
47687                     }
47688                 }
47689             }
47690             
47691             cfg.cn = [
47692                 label,
47693                 container
47694             ];
47695             
47696             var settings = this;
47697             
47698             ['xs','sm','md','lg'].map(function(size){
47699                 if (settings[size]) {
47700                     cfg.cls += ' col-' + size + '-' + settings[size];
47701                 }
47702             });
47703             
47704             this.store = new Roo.data.Store({
47705                 proxy : new Roo.data.MemoryProxy({}),
47706                 reader : new Roo.data.JsonReader({
47707                     fields : [
47708                         {
47709                             'name' : 'name',
47710                             'type' : 'string'
47711                         },
47712                         {
47713                             'name' : 'iso2',
47714                             'type' : 'string'
47715                         },
47716                         {
47717                             'name' : 'dialCode',
47718                             'type' : 'string'
47719                         },
47720                         {
47721                             'name' : 'priority',
47722                             'type' : 'string'
47723                         },
47724                         {
47725                             'name' : 'areaCodes',
47726                             'type' : 'string'
47727                         }
47728                     ]
47729                 })
47730             });
47731             
47732             if(!this.preferedCountries) {
47733                 this.preferedCountries = [
47734                     'hk',
47735                     'gb',
47736                     'us'
47737                 ];
47738             }
47739             
47740             var p = this.preferedCountries.reverse();
47741             
47742             if(p) {
47743                 for (var i = 0; i < p.length; i++) {
47744                     for (var j = 0; j < this.allCountries.length; j++) {
47745                         if(this.allCountries[j].iso2 == p[i]) {
47746                             var t = this.allCountries[j];
47747                             this.allCountries.splice(j,1);
47748                             this.allCountries.unshift(t);
47749                         }
47750                     } 
47751                 }
47752             }
47753             
47754             this.store.proxy.data = {
47755                 success: true,
47756                 data: this.allCountries
47757             };
47758             
47759             return cfg;
47760         },
47761         
47762         initEvents : function()
47763         {
47764             this.createList();
47765             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
47766             
47767             this.indicator = this.indicatorEl();
47768             this.flag = this.flagEl();
47769             this.dialCodeHolder = this.dialCodeHolderEl();
47770             
47771             this.trigger = this.el.select('div.flag-box',true).first();
47772             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
47773             
47774             var _this = this;
47775             
47776             (function(){
47777                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47778                 _this.list.setWidth(lw);
47779             }).defer(100);
47780             
47781             this.list.on('mouseover', this.onViewOver, this);
47782             this.list.on('mousemove', this.onViewMove, this);
47783             this.inputEl().on("keyup", this.onKeyUp, this);
47784             this.inputEl().on("keypress", this.onKeyPress, this);
47785             
47786             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
47787
47788             this.view = new Roo.View(this.list, this.tpl, {
47789                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47790             });
47791             
47792             this.view.on('click', this.onViewClick, this);
47793             this.setValue(this.defaultDialCode);
47794         },
47795         
47796         onTriggerClick : function(e)
47797         {
47798             Roo.log('trigger click');
47799             if(this.disabled){
47800                 return;
47801             }
47802             
47803             if(this.isExpanded()){
47804                 this.collapse();
47805                 this.hasFocus = false;
47806             }else {
47807                 this.store.load({});
47808                 this.hasFocus = true;
47809                 this.expand();
47810             }
47811         },
47812         
47813         isExpanded : function()
47814         {
47815             return this.list.isVisible();
47816         },
47817         
47818         collapse : function()
47819         {
47820             if(!this.isExpanded()){
47821                 return;
47822             }
47823             this.list.hide();
47824             Roo.get(document).un('mousedown', this.collapseIf, this);
47825             Roo.get(document).un('mousewheel', this.collapseIf, this);
47826             this.fireEvent('collapse', this);
47827             this.validate();
47828         },
47829         
47830         expand : function()
47831         {
47832             Roo.log('expand');
47833
47834             if(this.isExpanded() || !this.hasFocus){
47835                 return;
47836             }
47837             
47838             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
47839             this.list.setWidth(lw);
47840             
47841             this.list.show();
47842             this.restrictHeight();
47843             
47844             Roo.get(document).on('mousedown', this.collapseIf, this);
47845             Roo.get(document).on('mousewheel', this.collapseIf, this);
47846             
47847             this.fireEvent('expand', this);
47848         },
47849         
47850         restrictHeight : function()
47851         {
47852             this.list.alignTo(this.inputEl(), this.listAlign);
47853             this.list.alignTo(this.inputEl(), this.listAlign);
47854         },
47855         
47856         onViewOver : function(e, t)
47857         {
47858             if(this.inKeyMode){
47859                 return;
47860             }
47861             var item = this.view.findItemFromChild(t);
47862             
47863             if(item){
47864                 var index = this.view.indexOf(item);
47865                 this.select(index, false);
47866             }
47867         },
47868
47869         // private
47870         onViewClick : function(view, doFocus, el, e)
47871         {
47872             var index = this.view.getSelectedIndexes()[0];
47873             
47874             var r = this.store.getAt(index);
47875             
47876             if(r){
47877                 this.onSelect(r, index);
47878             }
47879             if(doFocus !== false && !this.blockFocus){
47880                 this.inputEl().focus();
47881             }
47882         },
47883         
47884         onViewMove : function(e, t)
47885         {
47886             this.inKeyMode = false;
47887         },
47888         
47889         select : function(index, scrollIntoView)
47890         {
47891             this.selectedIndex = index;
47892             this.view.select(index);
47893             if(scrollIntoView !== false){
47894                 var el = this.view.getNode(index);
47895                 if(el){
47896                     this.list.scrollChildIntoView(el, false);
47897                 }
47898             }
47899         },
47900         
47901         createList : function()
47902         {
47903             this.list = Roo.get(document.body).createChild({
47904                 tag: 'ul',
47905                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
47906                 style: 'display:none'
47907             });
47908             
47909             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
47910         },
47911         
47912         collapseIf : function(e)
47913         {
47914             var in_combo  = e.within(this.el);
47915             var in_list =  e.within(this.list);
47916             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
47917             
47918             if (in_combo || in_list || is_list) {
47919                 return;
47920             }
47921             this.collapse();
47922         },
47923         
47924         onSelect : function(record, index)
47925         {
47926             if(this.fireEvent('beforeselect', this, record, index) !== false){
47927                 
47928                 this.setFlagClass(record.data.iso2);
47929                 this.setDialCode(record.data.dialCode);
47930                 this.hasFocus = false;
47931                 this.collapse();
47932                 this.fireEvent('select', this, record, index);
47933             }
47934         },
47935         
47936         flagEl : function()
47937         {
47938             var flag = this.el.select('div.flag',true).first();
47939             if(!flag){
47940                 return false;
47941             }
47942             return flag;
47943         },
47944         
47945         dialCodeHolderEl : function()
47946         {
47947             var d = this.el.select('input.dial-code-holder',true).first();
47948             if(!d){
47949                 return false;
47950             }
47951             return d;
47952         },
47953         
47954         setDialCode : function(v)
47955         {
47956             this.dialCodeHolder.dom.value = '+'+v;
47957         },
47958         
47959         setFlagClass : function(n)
47960         {
47961             this.flag.dom.className = 'flag '+n;
47962         },
47963         
47964         getValue : function()
47965         {
47966             var v = this.inputEl().getValue();
47967             if(this.dialCodeHolder) {
47968                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
47969             }
47970             return v;
47971         },
47972         
47973         setValue : function(v)
47974         {
47975             var d = this.getDialCode(v);
47976             
47977             //invalid dial code
47978             if(v.length == 0 || !d || d.length == 0) {
47979                 if(this.rendered){
47980                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
47981                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47982                 }
47983                 return;
47984             }
47985             
47986             //valid dial code
47987             this.setFlagClass(this.dialCodeMapping[d].iso2);
47988             this.setDialCode(d);
47989             this.inputEl().dom.value = v.replace('+'+d,'');
47990             this.hiddenEl().dom.value = this.getValue();
47991             
47992             this.validate();
47993         },
47994         
47995         getDialCode : function(v)
47996         {
47997             v = v ||  '';
47998             
47999             if (v.length == 0) {
48000                 return this.dialCodeHolder.dom.value;
48001             }
48002             
48003             var dialCode = "";
48004             if (v.charAt(0) != "+") {
48005                 return false;
48006             }
48007             var numericChars = "";
48008             for (var i = 1; i < v.length; i++) {
48009               var c = v.charAt(i);
48010               if (!isNaN(c)) {
48011                 numericChars += c;
48012                 if (this.dialCodeMapping[numericChars]) {
48013                   dialCode = v.substr(1, i);
48014                 }
48015                 if (numericChars.length == 4) {
48016                   break;
48017                 }
48018               }
48019             }
48020             return dialCode;
48021         },
48022         
48023         reset : function()
48024         {
48025             this.setValue(this.defaultDialCode);
48026             this.validate();
48027         },
48028         
48029         hiddenEl : function()
48030         {
48031             return this.el.select('input.hidden-tel-input',true).first();
48032         },
48033         
48034         // after setting val
48035         onKeyUp : function(e){
48036             this.setValue(this.getValue());
48037         },
48038         
48039         onKeyPress : function(e){
48040             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48041                 e.stopEvent();
48042             }
48043         }
48044         
48045 });
48046 /**
48047  * @class Roo.bootstrap.form.MoneyField
48048  * @extends Roo.bootstrap.form.ComboBox
48049  * Bootstrap MoneyField class
48050  * 
48051  * @constructor
48052  * Create a new MoneyField.
48053  * @param {Object} config Configuration options
48054  */
48055
48056 Roo.bootstrap.form.MoneyField = function(config) {
48057     
48058     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48059     
48060 };
48061
48062 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48063     
48064     /**
48065      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48066      */
48067     allowDecimals : true,
48068     /**
48069      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48070      */
48071     decimalSeparator : ".",
48072     /**
48073      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48074      */
48075     decimalPrecision : 0,
48076     /**
48077      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48078      */
48079     allowNegative : true,
48080     /**
48081      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48082      */
48083     allowZero: true,
48084     /**
48085      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48086      */
48087     minValue : Number.NEGATIVE_INFINITY,
48088     /**
48089      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48090      */
48091     maxValue : Number.MAX_VALUE,
48092     /**
48093      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48094      */
48095     minText : "The minimum value for this field is {0}",
48096     /**
48097      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48098      */
48099     maxText : "The maximum value for this field is {0}",
48100     /**
48101      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48102      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48103      */
48104     nanText : "{0} is not a valid number",
48105     /**
48106      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48107      */
48108     castInt : true,
48109     /**
48110      * @cfg {String} defaults currency of the MoneyField
48111      * value should be in lkey
48112      */
48113     defaultCurrency : false,
48114     /**
48115      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48116      */
48117     thousandsDelimiter : false,
48118     /**
48119      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48120      */
48121     max_length: false,
48122     
48123     inputlg : 9,
48124     inputmd : 9,
48125     inputsm : 9,
48126     inputxs : 6,
48127      /**
48128      * @cfg {Roo.data.Store} store  Store to lookup currency??
48129      */
48130     store : false,
48131     
48132     getAutoCreate : function()
48133     {
48134         var align = this.labelAlign || this.parentLabelAlign();
48135         
48136         var id = Roo.id();
48137
48138         var cfg = {
48139             cls: 'form-group',
48140             cn: []
48141         };
48142
48143         var input =  {
48144             tag: 'input',
48145             id : id,
48146             cls : 'form-control roo-money-amount-input',
48147             autocomplete: 'new-password'
48148         };
48149         
48150         var hiddenInput = {
48151             tag: 'input',
48152             type: 'hidden',
48153             id: Roo.id(),
48154             cls: 'hidden-number-input'
48155         };
48156         
48157         if(this.max_length) {
48158             input.maxlength = this.max_length; 
48159         }
48160         
48161         if (this.name) {
48162             hiddenInput.name = this.name;
48163         }
48164
48165         if (this.disabled) {
48166             input.disabled = true;
48167         }
48168
48169         var clg = 12 - this.inputlg;
48170         var cmd = 12 - this.inputmd;
48171         var csm = 12 - this.inputsm;
48172         var cxs = 12 - this.inputxs;
48173         
48174         var container = {
48175             tag : 'div',
48176             cls : 'row roo-money-field',
48177             cn : [
48178                 {
48179                     tag : 'div',
48180                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48181                     cn : [
48182                         {
48183                             tag : 'div',
48184                             cls: 'roo-select2-container input-group',
48185                             cn: [
48186                                 {
48187                                     tag : 'input',
48188                                     cls : 'form-control roo-money-currency-input',
48189                                     autocomplete: 'new-password',
48190                                     readOnly : 1,
48191                                     name : this.currencyName
48192                                 },
48193                                 {
48194                                     tag :'span',
48195                                     cls : 'input-group-addon',
48196                                     cn : [
48197                                         {
48198                                             tag: 'span',
48199                                             cls: 'caret'
48200                                         }
48201                                     ]
48202                                 }
48203                             ]
48204                         }
48205                     ]
48206                 },
48207                 {
48208                     tag : 'div',
48209                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48210                     cn : [
48211                         {
48212                             tag: 'div',
48213                             cls: this.hasFeedback ? 'has-feedback' : '',
48214                             cn: [
48215                                 input
48216                             ]
48217                         }
48218                     ]
48219                 }
48220             ]
48221             
48222         };
48223         
48224         if (this.fieldLabel.length) {
48225             var indicator = {
48226                 tag: 'i',
48227                 tooltip: 'This field is required'
48228             };
48229
48230             var label = {
48231                 tag: 'label',
48232                 'for':  id,
48233                 cls: 'control-label',
48234                 cn: []
48235             };
48236
48237             var label_text = {
48238                 tag: 'span',
48239                 html: this.fieldLabel
48240             };
48241
48242             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48243             label.cn = [
48244                 indicator,
48245                 label_text
48246             ];
48247
48248             if(this.indicatorpos == 'right') {
48249                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48250                 label.cn = [
48251                     label_text,
48252                     indicator
48253                 ];
48254             }
48255
48256             if(align == 'left') {
48257                 container = {
48258                     tag: 'div',
48259                     cn: [
48260                         container
48261                     ]
48262                 };
48263
48264                 if(this.labelWidth > 12){
48265                     label.style = "width: " + this.labelWidth + 'px';
48266                 }
48267                 if(this.labelWidth < 13 && this.labelmd == 0){
48268                     this.labelmd = this.labelWidth;
48269                 }
48270                 if(this.labellg > 0){
48271                     label.cls += ' col-lg-' + this.labellg;
48272                     input.cls += ' col-lg-' + (12 - this.labellg);
48273                 }
48274                 if(this.labelmd > 0){
48275                     label.cls += ' col-md-' + this.labelmd;
48276                     container.cls += ' col-md-' + (12 - this.labelmd);
48277                 }
48278                 if(this.labelsm > 0){
48279                     label.cls += ' col-sm-' + this.labelsm;
48280                     container.cls += ' col-sm-' + (12 - this.labelsm);
48281                 }
48282                 if(this.labelxs > 0){
48283                     label.cls += ' col-xs-' + this.labelxs;
48284                     container.cls += ' col-xs-' + (12 - this.labelxs);
48285                 }
48286             }
48287         }
48288
48289         cfg.cn = [
48290             label,
48291             container,
48292             hiddenInput
48293         ];
48294         
48295         var settings = this;
48296
48297         ['xs','sm','md','lg'].map(function(size){
48298             if (settings[size]) {
48299                 cfg.cls += ' col-' + size + '-' + settings[size];
48300             }
48301         });
48302         
48303         return cfg;
48304     },
48305     
48306     initEvents : function()
48307     {
48308         this.indicator = this.indicatorEl();
48309         
48310         this.initCurrencyEvent();
48311         
48312         this.initNumberEvent();
48313     },
48314     
48315     initCurrencyEvent : function()
48316     {
48317         if (!this.store) {
48318             throw "can not find store for combo";
48319         }
48320         
48321         this.store = Roo.factory(this.store, Roo.data);
48322         this.store.parent = this;
48323         
48324         this.createList();
48325         
48326         this.triggerEl = this.el.select('.input-group-addon', true).first();
48327         
48328         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48329         
48330         var _this = this;
48331         
48332         (function(){
48333             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48334             _this.list.setWidth(lw);
48335         }).defer(100);
48336         
48337         this.list.on('mouseover', this.onViewOver, this);
48338         this.list.on('mousemove', this.onViewMove, this);
48339         this.list.on('scroll', this.onViewScroll, this);
48340         
48341         if(!this.tpl){
48342             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48343         }
48344         
48345         this.view = new Roo.View(this.list, this.tpl, {
48346             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48347         });
48348         
48349         this.view.on('click', this.onViewClick, this);
48350         
48351         this.store.on('beforeload', this.onBeforeLoad, this);
48352         this.store.on('load', this.onLoad, this);
48353         this.store.on('loadexception', this.onLoadException, this);
48354         
48355         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48356             "up" : function(e){
48357                 this.inKeyMode = true;
48358                 this.selectPrev();
48359             },
48360
48361             "down" : function(e){
48362                 if(!this.isExpanded()){
48363                     this.onTriggerClick();
48364                 }else{
48365                     this.inKeyMode = true;
48366                     this.selectNext();
48367                 }
48368             },
48369
48370             "enter" : function(e){
48371                 this.collapse();
48372                 
48373                 if(this.fireEvent("specialkey", this, e)){
48374                     this.onViewClick(false);
48375                 }
48376                 
48377                 return true;
48378             },
48379
48380             "esc" : function(e){
48381                 this.collapse();
48382             },
48383
48384             "tab" : function(e){
48385                 this.collapse();
48386                 
48387                 if(this.fireEvent("specialkey", this, e)){
48388                     this.onViewClick(false);
48389                 }
48390                 
48391                 return true;
48392             },
48393
48394             scope : this,
48395
48396             doRelay : function(foo, bar, hname){
48397                 if(hname == 'down' || this.scope.isExpanded()){
48398                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48399                 }
48400                 return true;
48401             },
48402
48403             forceKeyDown: true
48404         });
48405         
48406         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48407         
48408     },
48409     
48410     initNumberEvent : function(e)
48411     {
48412         this.inputEl().on("keydown" , this.fireKey,  this);
48413         this.inputEl().on("focus", this.onFocus,  this);
48414         this.inputEl().on("blur", this.onBlur,  this);
48415         
48416         this.inputEl().relayEvent('keyup', this);
48417         
48418         if(this.indicator){
48419             this.indicator.addClass('invisible');
48420         }
48421  
48422         this.originalValue = this.getValue();
48423         
48424         if(this.validationEvent == 'keyup'){
48425             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48426             this.inputEl().on('keyup', this.filterValidation, this);
48427         }
48428         else if(this.validationEvent !== false){
48429             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48430         }
48431         
48432         if(this.selectOnFocus){
48433             this.on("focus", this.preFocus, this);
48434             
48435         }
48436         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48437             this.inputEl().on("keypress", this.filterKeys, this);
48438         } else {
48439             this.inputEl().relayEvent('keypress', this);
48440         }
48441         
48442         var allowed = "0123456789";
48443         
48444         if(this.allowDecimals){
48445             allowed += this.decimalSeparator;
48446         }
48447         
48448         if(this.allowNegative){
48449             allowed += "-";
48450         }
48451         
48452         if(this.thousandsDelimiter) {
48453             allowed += ",";
48454         }
48455         
48456         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48457         
48458         var keyPress = function(e){
48459             
48460             var k = e.getKey();
48461             
48462             var c = e.getCharCode();
48463             
48464             if(
48465                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48466                     allowed.indexOf(String.fromCharCode(c)) === -1
48467             ){
48468                 e.stopEvent();
48469                 return;
48470             }
48471             
48472             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48473                 return;
48474             }
48475             
48476             if(allowed.indexOf(String.fromCharCode(c)) === -1){
48477                 e.stopEvent();
48478             }
48479         };
48480         
48481         this.inputEl().on("keypress", keyPress, this);
48482         
48483     },
48484     
48485     onTriggerClick : function(e)
48486     {   
48487         if(this.disabled){
48488             return;
48489         }
48490         
48491         this.page = 0;
48492         this.loadNext = false;
48493         
48494         if(this.isExpanded()){
48495             this.collapse();
48496             return;
48497         }
48498         
48499         this.hasFocus = true;
48500         
48501         if(this.triggerAction == 'all') {
48502             this.doQuery(this.allQuery, true);
48503             return;
48504         }
48505         
48506         this.doQuery(this.getRawValue());
48507     },
48508     
48509     getCurrency : function()
48510     {   
48511         var v = this.currencyEl().getValue();
48512         
48513         return v;
48514     },
48515     
48516     restrictHeight : function()
48517     {
48518         this.list.alignTo(this.currencyEl(), this.listAlign);
48519         this.list.alignTo(this.currencyEl(), this.listAlign);
48520     },
48521     
48522     onViewClick : function(view, doFocus, el, e)
48523     {
48524         var index = this.view.getSelectedIndexes()[0];
48525         
48526         var r = this.store.getAt(index);
48527         
48528         if(r){
48529             this.onSelect(r, index);
48530         }
48531     },
48532     
48533     onSelect : function(record, index){
48534         
48535         if(this.fireEvent('beforeselect', this, record, index) !== false){
48536         
48537             this.setFromCurrencyData(index > -1 ? record.data : false);
48538             
48539             this.collapse();
48540             
48541             this.fireEvent('select', this, record, index);
48542         }
48543     },
48544     
48545     setFromCurrencyData : function(o)
48546     {
48547         var currency = '';
48548         
48549         this.lastCurrency = o;
48550         
48551         if (this.currencyField) {
48552             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
48553         } else {
48554             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
48555         }
48556         
48557         this.lastSelectionText = currency;
48558         
48559         //setting default currency
48560         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
48561             this.setCurrency(this.defaultCurrency);
48562             return;
48563         }
48564         
48565         this.setCurrency(currency);
48566     },
48567     
48568     setFromData : function(o)
48569     {
48570         var c = {};
48571         
48572         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
48573         
48574         this.setFromCurrencyData(c);
48575         
48576         var value = '';
48577         
48578         if (this.name) {
48579             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
48580         } else {
48581             Roo.log('no value set for '+ (this.name ? this.name : this.id));
48582         }
48583         
48584         this.setValue(value);
48585         
48586     },
48587     
48588     setCurrency : function(v)
48589     {   
48590         this.currencyValue = v;
48591         
48592         if(this.rendered){
48593             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
48594             this.validate();
48595         }
48596     },
48597     
48598     setValue : function(v)
48599     {
48600         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
48601         
48602         this.value = v;
48603         
48604         if(this.rendered){
48605             
48606             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48607             
48608             this.inputEl().dom.value = (v == '') ? '' :
48609                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
48610             
48611             if(!this.allowZero && v === '0') {
48612                 this.hiddenEl().dom.value = '';
48613                 this.inputEl().dom.value = '';
48614             }
48615             
48616             this.validate();
48617         }
48618     },
48619     
48620     getRawValue : function()
48621     {
48622         var v = this.inputEl().getValue();
48623         
48624         return v;
48625     },
48626     
48627     getValue : function()
48628     {
48629         return this.fixPrecision(this.parseValue(this.getRawValue()));
48630     },
48631     
48632     parseValue : function(value)
48633     {
48634         if(this.thousandsDelimiter) {
48635             value += "";
48636             r = new RegExp(",", "g");
48637             value = value.replace(r, "");
48638         }
48639         
48640         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
48641         return isNaN(value) ? '' : value;
48642         
48643     },
48644     
48645     fixPrecision : function(value)
48646     {
48647         if(this.thousandsDelimiter) {
48648             value += "";
48649             r = new RegExp(",", "g");
48650             value = value.replace(r, "");
48651         }
48652         
48653         var nan = isNaN(value);
48654         
48655         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
48656             return nan ? '' : value;
48657         }
48658         return parseFloat(value).toFixed(this.decimalPrecision);
48659     },
48660     
48661     decimalPrecisionFcn : function(v)
48662     {
48663         return Math.floor(v);
48664     },
48665     
48666     validateValue : function(value)
48667     {
48668         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
48669             return false;
48670         }
48671         
48672         var num = this.parseValue(value);
48673         
48674         if(isNaN(num)){
48675             this.markInvalid(String.format(this.nanText, value));
48676             return false;
48677         }
48678         
48679         if(num < this.minValue){
48680             this.markInvalid(String.format(this.minText, this.minValue));
48681             return false;
48682         }
48683         
48684         if(num > this.maxValue){
48685             this.markInvalid(String.format(this.maxText, this.maxValue));
48686             return false;
48687         }
48688         
48689         return true;
48690     },
48691     
48692     validate : function()
48693     {
48694         if(this.disabled || this.allowBlank){
48695             this.markValid();
48696             return true;
48697         }
48698         
48699         var currency = this.getCurrency();
48700         
48701         if(this.validateValue(this.getRawValue()) && currency.length){
48702             this.markValid();
48703             return true;
48704         }
48705         
48706         this.markInvalid();
48707         return false;
48708     },
48709     
48710     getName: function()
48711     {
48712         return this.name;
48713     },
48714     
48715     beforeBlur : function()
48716     {
48717         if(!this.castInt){
48718             return;
48719         }
48720         
48721         var v = this.parseValue(this.getRawValue());
48722         
48723         if(v || v == 0){
48724             this.setValue(v);
48725         }
48726     },
48727     
48728     onBlur : function()
48729     {
48730         this.beforeBlur();
48731         
48732         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
48733             //this.el.removeClass(this.focusClass);
48734         }
48735         
48736         this.hasFocus = false;
48737         
48738         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
48739             this.validate();
48740         }
48741         
48742         var v = this.getValue();
48743         
48744         if(String(v) !== String(this.startValue)){
48745             this.fireEvent('change', this, v, this.startValue);
48746         }
48747         
48748         this.fireEvent("blur", this);
48749     },
48750     
48751     inputEl : function()
48752     {
48753         return this.el.select('.roo-money-amount-input', true).first();
48754     },
48755     
48756     currencyEl : function()
48757     {
48758         return this.el.select('.roo-money-currency-input', true).first();
48759     },
48760     
48761     hiddenEl : function()
48762     {
48763         return this.el.select('input.hidden-number-input',true).first();
48764     }
48765     
48766 });/**
48767  * @class Roo.bootstrap.BezierSignature
48768  * @extends Roo.bootstrap.Component
48769  * Bootstrap BezierSignature class
48770  * This script refer to:
48771  *    Title: Signature Pad
48772  *    Author: szimek
48773  *    Availability: https://github.com/szimek/signature_pad
48774  *
48775  * @constructor
48776  * Create a new BezierSignature
48777  * @param {Object} config The config object
48778  */
48779
48780 Roo.bootstrap.BezierSignature = function(config){
48781     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
48782     this.addEvents({
48783         "resize" : true
48784     });
48785 };
48786
48787 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
48788 {
48789      
48790     curve_data: [],
48791     
48792     is_empty: true,
48793     
48794     mouse_btn_down: true,
48795     
48796     /**
48797      * @cfg {int} canvas height
48798      */
48799     canvas_height: '200px',
48800     
48801     /**
48802      * @cfg {float|function} Radius of a single dot.
48803      */ 
48804     dot_size: false,
48805     
48806     /**
48807      * @cfg {float} Minimum width of a line. Defaults to 0.5.
48808      */
48809     min_width: 0.5,
48810     
48811     /**
48812      * @cfg {float} Maximum width of a line. Defaults to 2.5.
48813      */
48814     max_width: 2.5,
48815     
48816     /**
48817      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
48818      */
48819     throttle: 16,
48820     
48821     /**
48822      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
48823      */
48824     min_distance: 5,
48825     
48826     /**
48827      * @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.
48828      */
48829     bg_color: 'rgba(0, 0, 0, 0)',
48830     
48831     /**
48832      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
48833      */
48834     dot_color: 'black',
48835     
48836     /**
48837      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
48838      */ 
48839     velocity_filter_weight: 0.7,
48840     
48841     /**
48842      * @cfg {function} Callback when stroke begin. 
48843      */
48844     onBegin: false,
48845     
48846     /**
48847      * @cfg {function} Callback when stroke end.
48848      */
48849     onEnd: false,
48850     
48851     getAutoCreate : function()
48852     {
48853         var cls = 'roo-signature column';
48854         
48855         if(this.cls){
48856             cls += ' ' + this.cls;
48857         }
48858         
48859         var col_sizes = [
48860             'lg',
48861             'md',
48862             'sm',
48863             'xs'
48864         ];
48865         
48866         for(var i = 0; i < col_sizes.length; i++) {
48867             if(this[col_sizes[i]]) {
48868                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
48869             }
48870         }
48871         
48872         var cfg = {
48873             tag: 'div',
48874             cls: cls,
48875             cn: [
48876                 {
48877                     tag: 'div',
48878                     cls: 'roo-signature-body',
48879                     cn: [
48880                         {
48881                             tag: 'canvas',
48882                             cls: 'roo-signature-body-canvas',
48883                             height: this.canvas_height,
48884                             width: this.canvas_width
48885                         }
48886                     ]
48887                 },
48888                 {
48889                     tag: 'input',
48890                     type: 'file',
48891                     style: 'display: none'
48892                 }
48893             ]
48894         };
48895         
48896         return cfg;
48897     },
48898     
48899     initEvents: function() 
48900     {
48901         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
48902         
48903         var canvas = this.canvasEl();
48904         
48905         // mouse && touch event swapping...
48906         canvas.dom.style.touchAction = 'none';
48907         canvas.dom.style.msTouchAction = 'none';
48908         
48909         this.mouse_btn_down = false;
48910         canvas.on('mousedown', this._handleMouseDown, this);
48911         canvas.on('mousemove', this._handleMouseMove, this);
48912         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
48913         
48914         if (window.PointerEvent) {
48915             canvas.on('pointerdown', this._handleMouseDown, this);
48916             canvas.on('pointermove', this._handleMouseMove, this);
48917             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
48918         }
48919         
48920         if ('ontouchstart' in window) {
48921             canvas.on('touchstart', this._handleTouchStart, this);
48922             canvas.on('touchmove', this._handleTouchMove, this);
48923             canvas.on('touchend', this._handleTouchEnd, this);
48924         }
48925         
48926         Roo.EventManager.onWindowResize(this.resize, this, true);
48927         
48928         // file input event
48929         this.fileEl().on('change', this.uploadImage, this);
48930         
48931         this.clear();
48932         
48933         this.resize();
48934     },
48935     
48936     resize: function(){
48937         
48938         var canvas = this.canvasEl().dom;
48939         var ctx = this.canvasElCtx();
48940         var img_data = false;
48941         
48942         if(canvas.width > 0) {
48943             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
48944         }
48945         // setting canvas width will clean img data
48946         canvas.width = 0;
48947         
48948         var style = window.getComputedStyle ? 
48949             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
48950             
48951         var padding_left = parseInt(style.paddingLeft) || 0;
48952         var padding_right = parseInt(style.paddingRight) || 0;
48953         
48954         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
48955         
48956         if(img_data) {
48957             ctx.putImageData(img_data, 0, 0);
48958         }
48959     },
48960     
48961     _handleMouseDown: function(e)
48962     {
48963         if (e.browserEvent.which === 1) {
48964             this.mouse_btn_down = true;
48965             this.strokeBegin(e);
48966         }
48967     },
48968     
48969     _handleMouseMove: function (e)
48970     {
48971         if (this.mouse_btn_down) {
48972             this.strokeMoveUpdate(e);
48973         }
48974     },
48975     
48976     _handleMouseUp: function (e)
48977     {
48978         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
48979             this.mouse_btn_down = false;
48980             this.strokeEnd(e);
48981         }
48982     },
48983     
48984     _handleTouchStart: function (e) {
48985         
48986         e.preventDefault();
48987         if (e.browserEvent.targetTouches.length === 1) {
48988             // var touch = e.browserEvent.changedTouches[0];
48989             // this.strokeBegin(touch);
48990             
48991              this.strokeBegin(e); // assume e catching the correct xy...
48992         }
48993     },
48994     
48995     _handleTouchMove: function (e) {
48996         e.preventDefault();
48997         // var touch = event.targetTouches[0];
48998         // _this._strokeMoveUpdate(touch);
48999         this.strokeMoveUpdate(e);
49000     },
49001     
49002     _handleTouchEnd: function (e) {
49003         var wasCanvasTouched = e.target === this.canvasEl().dom;
49004         if (wasCanvasTouched) {
49005             e.preventDefault();
49006             // var touch = event.changedTouches[0];
49007             // _this._strokeEnd(touch);
49008             this.strokeEnd(e);
49009         }
49010     },
49011     
49012     reset: function () {
49013         this._lastPoints = [];
49014         this._lastVelocity = 0;
49015         this._lastWidth = (this.min_width + this.max_width) / 2;
49016         this.canvasElCtx().fillStyle = this.dot_color;
49017     },
49018     
49019     strokeMoveUpdate: function(e)
49020     {
49021         this.strokeUpdate(e);
49022         
49023         if (this.throttle) {
49024             this.throttleStroke(this.strokeUpdate, this.throttle);
49025         }
49026         else {
49027             this.strokeUpdate(e);
49028         }
49029     },
49030     
49031     strokeBegin: function(e)
49032     {
49033         var newPointGroup = {
49034             color: this.dot_color,
49035             points: []
49036         };
49037         
49038         if (typeof this.onBegin === 'function') {
49039             this.onBegin(e);
49040         }
49041         
49042         this.curve_data.push(newPointGroup);
49043         this.reset();
49044         this.strokeUpdate(e);
49045     },
49046     
49047     strokeUpdate: function(e)
49048     {
49049         var rect = this.canvasEl().dom.getBoundingClientRect();
49050         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49051         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49052         var lastPoints = lastPointGroup.points;
49053         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49054         var isLastPointTooClose = lastPoint
49055             ? point.distanceTo(lastPoint) <= this.min_distance
49056             : false;
49057         var color = lastPointGroup.color;
49058         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49059             var curve = this.addPoint(point);
49060             if (!lastPoint) {
49061                 this.drawDot({color: color, point: point});
49062             }
49063             else if (curve) {
49064                 this.drawCurve({color: color, curve: curve});
49065             }
49066             lastPoints.push({
49067                 time: point.time,
49068                 x: point.x,
49069                 y: point.y
49070             });
49071         }
49072     },
49073     
49074     strokeEnd: function(e)
49075     {
49076         this.strokeUpdate(e);
49077         if (typeof this.onEnd === 'function') {
49078             this.onEnd(e);
49079         }
49080     },
49081     
49082     addPoint:  function (point) {
49083         var _lastPoints = this._lastPoints;
49084         _lastPoints.push(point);
49085         if (_lastPoints.length > 2) {
49086             if (_lastPoints.length === 3) {
49087                 _lastPoints.unshift(_lastPoints[0]);
49088             }
49089             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49090             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49091             _lastPoints.shift();
49092             return curve;
49093         }
49094         return null;
49095     },
49096     
49097     calculateCurveWidths: function (startPoint, endPoint) {
49098         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49099             (1 - this.velocity_filter_weight) * this._lastVelocity;
49100
49101         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49102         var widths = {
49103             end: newWidth,
49104             start: this._lastWidth
49105         };
49106         
49107         this._lastVelocity = velocity;
49108         this._lastWidth = newWidth;
49109         return widths;
49110     },
49111     
49112     drawDot: function (_a) {
49113         var color = _a.color, point = _a.point;
49114         var ctx = this.canvasElCtx();
49115         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49116         ctx.beginPath();
49117         this.drawCurveSegment(point.x, point.y, width);
49118         ctx.closePath();
49119         ctx.fillStyle = color;
49120         ctx.fill();
49121     },
49122     
49123     drawCurve: function (_a) {
49124         var color = _a.color, curve = _a.curve;
49125         var ctx = this.canvasElCtx();
49126         var widthDelta = curve.endWidth - curve.startWidth;
49127         var drawSteps = Math.floor(curve.length()) * 2;
49128         ctx.beginPath();
49129         ctx.fillStyle = color;
49130         for (var i = 0; i < drawSteps; i += 1) {
49131         var t = i / drawSteps;
49132         var tt = t * t;
49133         var ttt = tt * t;
49134         var u = 1 - t;
49135         var uu = u * u;
49136         var uuu = uu * u;
49137         var x = uuu * curve.startPoint.x;
49138         x += 3 * uu * t * curve.control1.x;
49139         x += 3 * u * tt * curve.control2.x;
49140         x += ttt * curve.endPoint.x;
49141         var y = uuu * curve.startPoint.y;
49142         y += 3 * uu * t * curve.control1.y;
49143         y += 3 * u * tt * curve.control2.y;
49144         y += ttt * curve.endPoint.y;
49145         var width = curve.startWidth + ttt * widthDelta;
49146         this.drawCurveSegment(x, y, width);
49147         }
49148         ctx.closePath();
49149         ctx.fill();
49150     },
49151     
49152     drawCurveSegment: function (x, y, width) {
49153         var ctx = this.canvasElCtx();
49154         ctx.moveTo(x, y);
49155         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49156         this.is_empty = false;
49157     },
49158     
49159     clear: function()
49160     {
49161         var ctx = this.canvasElCtx();
49162         var canvas = this.canvasEl().dom;
49163         ctx.fillStyle = this.bg_color;
49164         ctx.clearRect(0, 0, canvas.width, canvas.height);
49165         ctx.fillRect(0, 0, canvas.width, canvas.height);
49166         this.curve_data = [];
49167         this.reset();
49168         this.is_empty = true;
49169     },
49170     
49171     fileEl: function()
49172     {
49173         return  this.el.select('input',true).first();
49174     },
49175     
49176     canvasEl: function()
49177     {
49178         return this.el.select('canvas',true).first();
49179     },
49180     
49181     canvasElCtx: function()
49182     {
49183         return this.el.select('canvas',true).first().dom.getContext('2d');
49184     },
49185     
49186     getImage: function(type)
49187     {
49188         if(this.is_empty) {
49189             return false;
49190         }
49191         
49192         // encryption ?
49193         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49194     },
49195     
49196     drawFromImage: function(img_src)
49197     {
49198         var img = new Image();
49199         
49200         img.onload = function(){
49201             this.canvasElCtx().drawImage(img, 0, 0);
49202         }.bind(this);
49203         
49204         img.src = img_src;
49205         
49206         this.is_empty = false;
49207     },
49208     
49209     selectImage: function()
49210     {
49211         this.fileEl().dom.click();
49212     },
49213     
49214     uploadImage: function(e)
49215     {
49216         var reader = new FileReader();
49217         
49218         reader.onload = function(e){
49219             var img = new Image();
49220             img.onload = function(){
49221                 this.reset();
49222                 this.canvasElCtx().drawImage(img, 0, 0);
49223             }.bind(this);
49224             img.src = e.target.result;
49225         }.bind(this);
49226         
49227         reader.readAsDataURL(e.target.files[0]);
49228     },
49229     
49230     // Bezier Point Constructor
49231     Point: (function () {
49232         function Point(x, y, time) {
49233             this.x = x;
49234             this.y = y;
49235             this.time = time || Date.now();
49236         }
49237         Point.prototype.distanceTo = function (start) {
49238             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49239         };
49240         Point.prototype.equals = function (other) {
49241             return this.x === other.x && this.y === other.y && this.time === other.time;
49242         };
49243         Point.prototype.velocityFrom = function (start) {
49244             return this.time !== start.time
49245             ? this.distanceTo(start) / (this.time - start.time)
49246             : 0;
49247         };
49248         return Point;
49249     }()),
49250     
49251     
49252     // Bezier Constructor
49253     Bezier: (function () {
49254         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49255             this.startPoint = startPoint;
49256             this.control2 = control2;
49257             this.control1 = control1;
49258             this.endPoint = endPoint;
49259             this.startWidth = startWidth;
49260             this.endWidth = endWidth;
49261         }
49262         Bezier.fromPoints = function (points, widths, scope) {
49263             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49264             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49265             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49266         };
49267         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49268             var dx1 = s1.x - s2.x;
49269             var dy1 = s1.y - s2.y;
49270             var dx2 = s2.x - s3.x;
49271             var dy2 = s2.y - s3.y;
49272             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49273             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49274             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49275             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49276             var dxm = m1.x - m2.x;
49277             var dym = m1.y - m2.y;
49278             var k = l2 / (l1 + l2);
49279             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49280             var tx = s2.x - cm.x;
49281             var ty = s2.y - cm.y;
49282             return {
49283                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49284                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49285             };
49286         };
49287         Bezier.prototype.length = function () {
49288             var steps = 10;
49289             var length = 0;
49290             var px;
49291             var py;
49292             for (var i = 0; i <= steps; i += 1) {
49293                 var t = i / steps;
49294                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49295                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49296                 if (i > 0) {
49297                     var xdiff = cx - px;
49298                     var ydiff = cy - py;
49299                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49300                 }
49301                 px = cx;
49302                 py = cy;
49303             }
49304             return length;
49305         };
49306         Bezier.prototype.point = function (t, start, c1, c2, end) {
49307             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49308             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49309             + (3.0 * c2 * (1.0 - t) * t * t)
49310             + (end * t * t * t);
49311         };
49312         return Bezier;
49313     }()),
49314     
49315     throttleStroke: function(fn, wait) {
49316       if (wait === void 0) { wait = 250; }
49317       var previous = 0;
49318       var timeout = null;
49319       var result;
49320       var storedContext;
49321       var storedArgs;
49322       var later = function () {
49323           previous = Date.now();
49324           timeout = null;
49325           result = fn.apply(storedContext, storedArgs);
49326           if (!timeout) {
49327               storedContext = null;
49328               storedArgs = [];
49329           }
49330       };
49331       return function wrapper() {
49332           var args = [];
49333           for (var _i = 0; _i < arguments.length; _i++) {
49334               args[_i] = arguments[_i];
49335           }
49336           var now = Date.now();
49337           var remaining = wait - (now - previous);
49338           storedContext = this;
49339           storedArgs = args;
49340           if (remaining <= 0 || remaining > wait) {
49341               if (timeout) {
49342                   clearTimeout(timeout);
49343                   timeout = null;
49344               }
49345               previous = now;
49346               result = fn.apply(storedContext, storedArgs);
49347               if (!timeout) {
49348                   storedContext = null;
49349                   storedArgs = [];
49350               }
49351           }
49352           else if (!timeout) {
49353               timeout = window.setTimeout(later, remaining);
49354           }
49355           return result;
49356       };
49357   }
49358   
49359 });
49360
49361  
49362
49363  // old names for form elements
49364 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49365 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49366 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49367 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49368 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49369 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49370 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49371 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49372 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49373 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49374 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49375 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49376 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49377 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49378 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49379 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49380 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49381 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49382 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49383 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49384 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49385 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49386 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49387 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49388 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49389 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49390
49391 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49392 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49393
49394 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49395 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49396
49397 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49398 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49399 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49400 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49401